Каков хороший способ переопределить несвободный интерфейс как беглый?

То, что я ищу, это несвободный класс:

class NonFluent {
    int i=0;
public:
    void setValue(int i) {this->i = i;}
    void multiplyValue(int i) {this->i *= i;}
    int getValue() {return this->i;}
};

Я хотел бы изменить методы void, чтобы они действительно возвращали ссылку на *this. Я знаю, что невозможно просто вывести, потому что мы не можем изменить только тип возвращаемого значения, потому что C++ не сможет различить вызовы функций.

Мы могли бы использовать композицию:

class Fluent {
    Fluent& setValue(int i) {var.setValue(i); return *this;}
    Fluent& multiplyValue(int i) {var.multiplyValue(i); return *this;}
    int getValue() {return var.getValue();}

private:
    NonFluent var;
};

Но это боль, если для начала есть много void методов. Мы также могли бы использовать редактор объектов, о котором я задал вопрос: functions-to-call">Является ли редактор объектов хорошим подходом, если необходимо вызвать несколько функций-членов?, но он имеет много недостатков.

Знаете ли вы какие-нибудь хорошие методы для этого? (без прямого изменения несвободного класса?)


person xinaiz    schedule 11.02.2017    source источник


Ответы (3)


Лучший подход, который вы могли бы использовать с Fluent, но не с NonFluent, состоит в том, чтобы сделать ваш класс неизменяемым, а его методы возвращали новые объекты с результатами модификации:

class Fluent {
    static Fluent withValue(int i) {
        NonFluent v;
        v.setValue(i);
        return Fluent(v);
    }
    Fluent multiplyValue(int i) const {
        Fluent res(var);
        res.var.multiplyValue(i);
        return res;
    }
    int getValue() const {return var.getValue;}
private:
    Fluent(const NonFluent& v) : var(v) {}
    NonFluent var;
};

Обратите внимание на метод статической фабрики, создающий Fluent объектов.

Код с использованием фабрики выглядит так:

int res = Fluent
    .withValue(5)
    .multiplyValue(2)
    .getValue();

Это дает вам возможность переработать API, включив в него методы, использующие другие текучие объекты, например:

Fluent multiply(const Fluent& other) const {
    Fluent res(var);
    res.var.multiplyValue(other.getValue());
    return res;
}

Общий результат заключается в том, что ваш API становится совместимым с параллелизмом, ничего не меняя в исходном NonFluent API.

person Sergey Kalinichenko    schedule 11.02.2017

Я некоторое время думал об этом, и мне удалось создать удобный синтаксис для подхода к наследованию, в котором беглые методы будут начинаться с f_.

Вот немного магии макросов:

#define FIFTHS(_1, _2, _3, _4, _5, NAME, ...) NAME
#define MODIFY_ARGS_1(_1_) _1_ _1
#define MODIFY_ARGS_2(_1_, _2_) _1_ _1, _2_ _2
#define MODIFY_ARGS_3(_1_, _2_, _x_) _1_ _1, _2_ _2, _x_ _3
#define MODIFY_ARGS_4(_1_, _2_, _x_, _x) _1_ _1,  _2_ _2, _x_ _3, _x _4
#define MODIFY_ARGS_5(_1_, _2_, _x_, _x, 
class Simple {
    int i=0;   
public:
    Simple() : Simple(0) {}
    Simple(int i) {this->i = i;}
    void setValue(int i) {this->i = i;}
    void multiplyValue(int i) {this->i *= i;}
    void halve() {this->i /= 2;}
    int getValue() {return this->i;}

};
) _1_ _1, _2_ _2, _x_ _3, _x _4,
class Simple {
    int i=0;   
public:
    Simple() : Simple(0) {}
    Simple(int i) {this->i = i;}
    void setValue(int i) {this->i = i;}
    void multiplyValue(int i) {this->i *= i;}
    void halve() {this->i /= 2;}
    int getValue() {return this->i;}

};
_5 #define MODIFY_ARGS(...) FIFTHS(__VA_ARGS__, MODIFY_ARGS_5, MODIFY_ARGS_4, MODIFY_ARGS_3, MODIFY_ARGS_2, MODIFY_ARGS_1)(__VA_ARGS__) #define SEQUENCE_1(_1_) _1 #define SEQUENCE_2(_1_, _2_) _1, _2 #define SEQUENCE_3(_1_, _2_, _x_) _1, _2, _3 #define SEQUENCE_4(_1_, _2_, _x_, _x) _1, _2, _3, _4 #define SEQUENCE_5(_1_, _2_, _x_, _x,
class Simple {
    int i=0;   
public:
    Simple() : Simple(0) {}
    Simple(int i) {this->i = i;}
    void setValue(int i) {this->i = i;}
    void multiplyValue(int i) {this->i *= i;}
    void halve() {this->i /= 2;}
    int getValue() {return this->i;}

};
) _1, _2, _3, _4, _5 #define SEQUENCE(...) FIFTHS(__VA_ARGS__, SEQUENCE_5, SEQUENCE_4, SEQUENCE_3, SEQUENCE_2, SEQUENCE_1)(__VA_ARGS__) #define FLUENTIZE_DERIVE() CONCATENATE(Fluent,CURRENT_BASE) : public CURRENT_BASE #define FLUENTIZE_METHOD(name, ...) CONCATENATE(Fluent,CURRENT_BASE)& f_ ## name (MODIFY_ARGS(__VA_ARGS__)) {name(SEQUENCE(__VA_ARGS__)); return *this;} #define FLUENTIZE_PROCEDURE(name) CONCATENATE(Fluent,CURRENT_BASE)& f_ ## name() {name(); return *this;} #define FLUENTIZE_DFLT_CONSTRUCTOR(name) CONCATENATE(Fluent,CURRENT_BASE)() {} #define FLUENTIZE_CONSTRUCTOR(...) CONCATENATE(Fluent,CURRENT_BASE)(MODIFY_ARGS(__VA_ARGS__)) : CURRENT_BASE(SEQUENCE(__VA_ARGS__)) {}

Примечание. _x_ обозначает тип, а _x — имя аргумента.

Теперь у нас есть несвободный класс:

class Simple {
    int i=0;   
public:
    Simple() : Simple(0) {}
    Simple(int i) {this->i = i;}
    void setValue(int i) {this->i = i;}
    void multiplyValue(int i) {this->i *= i;}
    void halve() {this->i /= 2;}
    int getValue() {return this->i;}

};

И вот как воссоздать беглый класс:

#define CURRENT_BASE Simple
class FLUENTIZE_DERIVE() {
public:
    FLUENTIZE_DFLT_CONSTRUCTOR()
    FLUENTIZE_CONSTRUCTOR(int)
    FLUENTIZE_METHOD(setValue, int)
    FLUENTIZE_METHOD(multiplyValue, int)
    FLUENTIZE_PROCEDURE(halve)
};
#undef CURRENT_BASE

При этом мы создали класс с именем FluentSimple с плавными методами f_setValue, f_multiplyValue и f_halve. С помощью некоторой магии макросов я автоматически назвал аргументы функции (аргументы именуются как последовательность _1, _2, _3 ..., до _5 для тестирования). Обратите внимание, что мне пришлось сделать другой макрос для методов, которые не принимают никаких аргументов, потому что метод макросов, который я использовал, не может работать с пустым макросом __VA_ARGS__.

Теперь вот как использовать беглый класс:

std::cout << 
FluentSimple()
    .f_setValue(10)
    .f_multiplyValue(10)
    .f_halve()
    .getValue() << std::endl;

Также обратите внимание, что с этой конструкцией свободный класс имеет доступ ко всем методам базового класса и некоторым функциям, таким как слоты и сигналы Qt.

Если у него есть недостатки, кроме использования макросов, дайте мне знать :)

person xinaiz    schedule 11.02.2017

В качестве другого ответа я выбрал другой подход, который приводит к минимальному добавлению кода:

Редактор:

template <class T>
struct EditorImpl
{
    T* ptr;

    template <class F>
    EditorImpl& operator()(F&& f)
    {
        f(ptr);
        return *this;
    }

    T* yield() const {
        return ptr;
    }
};

template <class T>
EditorImpl<T> Editor(T* ptr) { return EditorImpl<T>{ptr}; }

template <class T>
EditorImpl<T> Editor(T& ref) { return EditorImpl<T>{&ref}; }

GET_MACRO (все до 100):

namespace detail {
#define GET_MACRO_1(_1, NAME, ...) NAME
#define GET_MACRO_2(_1, _2, NAME, ...) NAME
#define GET_MACRO_3(_1, _2, _3, NAME, ...) NAME
//...
#define GET_MACRO_98(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, _65, _66, _67, _68, _69, _70, _71, _72, _73, _74, _75, _76, _77, _78, _79, _80, _81, _82, _83, _84, _85, _86, _87, _88, _89, _90, _91, _92, _93, _94, _95, _96, _97, _98, NAME, ...) NAME
#define GET_MACRO_99(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, _65, _66, _67, _68, _69, _70, _71, _72, _73, _74, _75, _76, _77, _78, _79, _80, _81, _82, _83, _84, _85, _86, _87, _88, _89, _90, _91, _92, _93, _94, _95, _96, _97, _98, _99, NAME, ...) NAME
#define GET_MACRO_100(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, _65, _66, _67, _68, _69, _70, _71, _72, _73, _74, _75, _76, _77, _78, _79, _80, _81, _82, _83, _84, _85, _86, _87, _88, _89, _90, _91, _92, _93, _94, _95, _96, _97, _98, _99, _100, NAME, ...) NAME
}

Макрос, который генерирует лямбду, которая вызывает аргумент макроса для объекта:

namespace detail {
#define E_1(_1)[](auto* p) {p -> _1; }
#define E_2(_1,_2)[](auto* p) {p -> _1; p -> _2; }
#define E_3(_1,_2,_3)[](auto* p) {p -> _1; p -> _2; p -> _3; }
//...    
#define E_98(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32,_33,_34,_35,_36,_37,_38,_39,_40,_41,_42,_43,_44,_45,_46,_47,_48,_49,_50,_51,_52,_53,_54,_55,_56,_57,_58,_59,_60,_61,_62,_63,_64,_65,_66,_67,_68,_69,_70,_71,_72,_73,_74,_75,_76,_77,_78,_79,_80,_81,_82,_83,_84,_85,_86,_87,_88,_89,_90,_91,_92,_93,_94,_95,_96,_97,_98)[](auto* p) {p -> _1; p -> _2; p -> _3; p -> _4; p -> _5; p -> _6; p -> _7; p -> _8; p -> _9; p -> _10; p -> _11; p -> _12; p -> _13; p -> _14; p -> _15; p -> _16; p -> _17; p -> _18; p -> _19; p -> _20; p -> _21; p -> _22; p -> _23; p -> _24; p -> _25; p -> _26; p -> _27; p -> _28; p -> _29; p -> _30; p -> _31; p -> _32; p -> _33; p -> _34; p -> _35; p -> _36; p -> _37; p -> _38; p -> _39; p -> _40; p -> _41; p -> _42; p -> _43; p -> _44; p -> _45; p -> _46; p -> _47; p -> _48; p -> _49; p -> _50; p -> _51; p -> _52; p -> _53; p -> _54; p -> _55; p -> _56; p -> _57; p -> _58; p -> _59; p -> _60; p -> _61; p -> _62; p -> _63; p -> _64; p -> _65; p -> _66; p -> _67; p -> _68; p -> _69; p -> _70; p -> _71; p -> _72; p -> _73; p -> _74; p -> _75; p -> _76; p -> _77; p -> _78; p -> _79; p -> _80; p -> _81; p -> _82; p -> _83; p -> _84; p -> _85; p -> _86; p -> _87; p -> _88; p -> _89; p -> _90; p -> _91; p -> _92; p -> _93; p -> _94; p -> _95; p -> _96; p -> _97; p -> _98; }
#define E_99(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32,_33,_34,_35,_36,_37,_38,_39,_40,_41,_42,_43,_44,_45,_46,_47,_48,_49,_50,_51,_52,_53,_54,_55,_56,_57,_58,_59,_60,_61,_62,_63,_64,_65,_66,_67,_68,_69,_70,_71,_72,_73,_74,_75,_76,_77,_78,_79,_80,_81,_82,_83,_84,_85,_86,_87,_88,_89,_90,_91,_92,_93,_94,_95,_96,_97,_98,_99)[](auto* p) {p -> _1; p -> _2; p -> _3; p -> _4; p -> _5; p -> _6; p -> _7; p -> _8; p -> _9; p -> _10; p -> _11; p -> _12; p -> _13; p -> _14; p -> _15; p -> _16; p -> _17; p -> _18; p -> _19; p -> _20; p -> _21; p -> _22; p -> _23; p -> _24; p -> _25; p -> _26; p -> _27; p -> _28; p -> _29; p -> _30; p -> _31; p -> _32; p -> _33; p -> _34; p -> _35; p -> _36; p -> _37; p -> _38; p -> _39; p -> _40; p -> _41; p -> _42; p -> _43; p -> _44; p -> _45; p -> _46; p -> _47; p -> _48; p -> _49; p -> _50; p -> _51; p -> _52; p -> _53; p -> _54; p -> _55; p -> _56; p -> _57; p -> _58; p -> _59; p -> _60; p -> _61; p -> _62; p -> _63; p -> _64; p -> _65; p -> _66; p -> _67; p -> _68; p -> _69; p -> _70; p -> _71; p -> _72; p -> _73; p -> _74; p -> _75; p -> _76; p -> _77; p -> _78; p -> _79; p -> _80; p -> _81; p -> _82; p -> _83; p -> _84; p -> _85; p -> _86; p -> _87; p -> _88; p -> _89; p -> _90; p -> _91; p -> _92; p -> _93; p -> _94; p -> _95; p -> _96; p -> _97; p -> _98; p -> _99; }
#define E_100(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32,_33,_34,_35,_36,_37,_38,_39,_40,_41,_42,_43,_44,_45,_46,_47,_48,_49,_50,_51,_52,_53,_54,_55,_56,_57,_58,_59,_60,_61,_62,_63,_64,_65,_66,_67,_68,_69,_70,_71,_72,_73,_74,_75,_76,_77,_78,_79,_80,_81,_82,_83,_84,_85,_86,_87,_88,_89,_90,_91,_92,_93,_94,_95,_96,_97,_98,_99,_100)[](auto* p) {p -> _1; p -> _2; p -> _3; p -> _4; p -> _5; p -> _6; p -> _7; p -> _8; p -> _9; p -> _10; p -> _11; p -> _12; p -> _13; p -> _14; p -> _15; p -> _16; p -> _17; p -> _18; p -> _19; p -> _20; p -> _21; p -> _22; p -> _23; p -> _24; p -> _25; p -> _26; p -> _27; p -> _28; p -> _29; p -> _30; p -> _31; p -> _32; p -> _33; p -> _34; p -> _35; p -> _36; p -> _37; p -> _38; p -> _39; p -> _40; p -> _41; p -> _42; p -> _43; p -> _44; p -> _45; p -> _46; p -> _47; p -> _48; p -> _49; p -> _50; p -> _51; p -> _52; p -> _53; p -> _54; p -> _55; p -> _56; p -> _57; p -> _58; p -> _59; p -> _60; p -> _61; p -> _62; p -> _63; p -> _64; p -> _65; p -> _66; p -> _67; p -> _68; p -> _69; p -> _70; p -> _71; p -> _72; p -> _73; p -> _74; p -> _75; p -> _76; p -> _77; p -> _78; p -> _79; p -> _80; p -> _81; p -> _82; p -> _83; p -> _84; p -> _85; p -> _86; p -> _87; p -> _88; p -> _89; p -> _90; p -> _91; p -> _92; p -> _93; p -> _94; p -> _95; p -> _96; p -> _97; p -> _98; p -> _99; p -> _100; }
}

#define E_(...) GET_MACRO_100(__VA_ARGS__, E_100, E_99, E_98, E_97, E_96, E_95, E_94, E_93, E_92, E_91, E_90, E_89, E_88, E_87, E_86, E_85, E_84, E_83, E_82, E_81, E_80, E_79, E_78, E_77, E_76, E_75, E_74, E_73, E_72, E_71, E_70, E_69, E_68, E_67, E_66, E_65, E_64, E_63, E_62, E_61, E_60, E_59, E_58, E_57, E_56, E_55, E_54, E_53, E_52, E_51, E_50, E_49, E_48, E_47, E_46, E_45, E_44, E_43, E_42, E_41, E_40, E_39, E_38, E_37, E_36, E_35, E_34, E_33, E_32, E_31, E_30, E_29, E_28, E_27, E_26, E_25, E_24, E_23, E_22, E_21, E_20, E_19, E_18, E_17, E_16, E_15, E_14, E_13, E_12, E_11, E_10, E_9, E_8, E_7, E_6, E_5, E_4, E_3, E_2, E_1)(__VA_ARGS__)

Финальный макрос:

namespace detail {
#define EDIT_0(_1 )Editor(_1).yield()
#define EDIT_1(_1,_2)Editor(_1)(E_(_2)).yield()
#define EDIT_2(_1,_2,_3)Editor(_1)(E_(_2,_3)).yield()
#define EDIT_3(_1,_2,_3,_4)Editor(_1)(E_(_2,_3,_4)).yield()
//...
#define EDIT_98(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32,_33,_34,_35,_36,_37,_38,_39,_40,_41,_42,_43,_44,_45,_46,_47,_48,_49,_50,_51,_52,_53,_54,_55,_56,_57,_58,_59,_60,_61,_62,_63,_64,_65,_66,_67,_68,_69,_70,_71,_72,_73,_74,_75,_76,_77,_78,_79,_80,_81,_82,_83,_84,_85,_86,_87,_88,_89,_90,_91,_92,_93,_94,_95,_96,_97,_98,_99)Editor(_1)(E_(_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32,_33,_34,_35,_36,_37,_38,_39,_40,_41,_42,_43,_44,_45,_46,_47,_48,_49,_50,_51,_52,_53,_54,_55,_56,_57,_58,_59,_60,_61,_62,_63,_64,_65,_66,_67,_68,_69,_70,_71,_72,_73,_74,_75,_76,_77,_78,_79,_80,_81,_82,_83,_84,_85,_86,_87,_88,_89,_90,_91,_92,_93,_94,_95,_96,_97,_98,_99)).yield()
#define EDIT_99(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32,_33,_34,_35,_36,_37,_38,_39,_40,_41,_42,_43,_44,_45,_46,_47,_48,_49,_50,_51,_52,_53,_54,_55,_56,_57,_58,_59,_60,_61,_62,_63,_64,_65,_66,_67,_68,_69,_70,_71,_72,_73,_74,_75,_76,_77,_78,_79,_80,_81,_82,_83,_84,_85,_86,_87,_88,_89,_90,_91,_92,_93,_94,_95,_96,_97,_98,_99,_100)Editor(_1)(E_(_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32,_33,_34,_35,_36,_37,_38,_39,_40,_41,_42,_43,_44,_45,_46,_47,_48,_49,_50,_51,_52,_53,_54,_55,_56,_57,_58,_59,_60,_61,_62,_63,_64,_65,_66,_67,_68,_69,_70,_71,_72,_73,_74,_75,_76,_77,_78,_79,_80,_81,_82,_83,_84,_85,_86,_87,_88,_89,_90,_91,_92,_93,_94,_95,_96,_97,_98,_99,_100)).yield()
}
#define EDIT(...) GET_MACRO_100(__VA_ARGS__, EDIT_99, EDIT_98, EDIT_97, EDIT_96, EDIT_95, EDIT_94, EDIT_93, EDIT_92, EDIT_91, EDIT_90, EDIT_89, EDIT_88, EDIT_87, EDIT_86, EDIT_85, EDIT_84, EDIT_83, EDIT_82, EDIT_81, EDIT_80, EDIT_79, EDIT_78, EDIT_77, EDIT_76, EDIT_75, EDIT_74, EDIT_73, EDIT_72, EDIT_71, EDIT_70, EDIT_69, EDIT_68, EDIT_67, EDIT_66, EDIT_65, EDIT_64, EDIT_63, EDIT_62, EDIT_61, EDIT_60, EDIT_59, EDIT_58, EDIT_57, EDIT_56, EDIT_55, EDIT_54, EDIT_53, EDIT_52, EDIT_51, EDIT_50, EDIT_49, EDIT_48, EDIT_47, EDIT_46, EDIT_45, EDIT_44, EDIT_43, EDIT_42, EDIT_41, EDIT_40, EDIT_39, EDIT_38, EDIT_37, EDIT_36, EDIT_35, EDIT_34, EDIT_33, EDIT_32, EDIT_31, EDIT_30, EDIT_29, EDIT_28, EDIT_27, EDIT_26, EDIT_25, EDIT_24, EDIT_23, EDIT_22, EDIT_21, EDIT_20, EDIT_19, EDIT_18, EDIT_17, EDIT_16, EDIT_15, EDIT_14, EDIT_13, EDIT_12, EDIT_11, EDIT_10, EDIT_9, EDIT_8, EDIT_7, EDIT_6, EDIT_5, EDIT_4, EDIT_3, EDIT_2, EDIT_1, EDIT_0)(__VA_ARGS__)

Пример класса:

class Simple {
    int i=0;
public:
    Simple() : Simple(0) {}
    Simple(int i) {this->i = i;}
    void setValue(int i) {this->i = i;}
    void multiplyValue(int i) {this->i *= i;}
    void halve() {this->i /= 2;}
    int getValue() {return this->i;}

};

И после всего этого безумия использование:

Simple* simple = EDIT(new Simple, setValue(50), multiplyValue(3));

Это лучшее, что я мог сделать :)

person xinaiz    schedule 17.02.2017