Виртуальный конструктор на шаблонах?
От: Tilir Россия http://tilir.livejournal.com
Дата: 06.08.14 12:12
Оценка: -2 :)
Hi,

Возник теоретический спор об эквивалентности шаблонного и динамического полиморфизма в C++

Был предложен такой контрпример тезису об эквивалентности как виртуальный конструктор:


class obj
{
public:
  virtual int foo (int) = 0;    
};

class A : public obj;
class B : public obj;
...

/* returns A is config have 'a', B is 'b', and so on */
obj * getfromconfig (const char *filename);

int
entry (obj *x)
{
  return x->foo();
}

int
main (void)
{
  return entry (getfromconfig("my.xml"))
}


Как бы вы переписали этот код с шаблонным полиморфизмом вместо виртуальных функций?

---
With best regards, Konstantin
Re: Виртуальный конструктор на шаблонах?
От: Kernan Ниоткуда https://rsdn.ru/forum/flame.politics/
Дата: 06.08.14 12:46
Оценка: +2
Здравствуйте, Tilir, Вы писали:

T>Как бы вы переписали этот код с шаблонным полиморфизмом вместо виртуальных функций?

Никак, если имя файла задаётся в рантайм.
Sic luceat lux!
Re: Виртуальный конструктор на шаблонах?
От: Abyx Россия  
Дата: 06.08.14 13:04
Оценка: -4
Здравствуйте, Tilir, Вы писали:

T>Hi,


T>Возник теоретический спор об эквивалентности шаблонного и динамического полиморфизма в C++


T>Был предложен такой контрпример тезису об эквивалентности как виртуальный конструктор:



T>
T>class obj
T>{
T>public:
T>  virtual int foo (int) = 0;    
T>};

T>class A : public obj;
T>class B : public obj;
T>...

T>/* returns A is config have 'a', B is 'b', and so on */
T>obj * getfromconfig (const char *filename);

T>int
T>entry (obj *x)
T>{
T>  return x->foo();
T>}

T>int
T>main (void)
T>{
T>  return entry (getfromconfig("my.xml"))
T>} 
T>


T>Как бы вы переписали этот код с шаблонным полиморфизмом вместо виртуальных функций?


T>---

T>With best regards, Konstantin

-1 за main(void) в С++

чтоб два раза не вставать — в коде нет реализации getfromconfig, которая содержит наиболее интересную часть кода.
In Zen We Trust
Re[2]: Виртуальный конструктор на шаблонах?
От: Tilir Россия http://tilir.livejournal.com
Дата: 06.08.14 13:16
Оценка:
Здравствуйте, Abyx, Вы писали:

A>чтоб два раза не вставать — в коде нет реализации getfromconfig, которая содержит наиболее интересную часть кода.


Я же написал коммент. Очевидный фабричный метод:

/* returns A if config have 'a', B is 'b', and so on */
obj * 
getfromconfig (const char *filename)
{
  fstream f (filename, ios_base::in);
  if (existsin (f, 'a'))
    return new A();
  if (existsin (f, 'b'))
    return new B();
  ...
}
Re[2]: Виртуальный конструктор на шаблонах?
От: Kernan Ниоткуда https://rsdn.ru/forum/flame.politics/
Дата: 06.08.14 13:32
Оценка: 8 (1)
Здравствуйте, Kernan, Вы писали:

K>Никак, если имя файла задаётся в рантайм.

Если хочется именно в компайлтайм, то что-то вроде этого. Но смысл? то, чего ты хочешь, ты не получишь.
struct obj
{
public:
  virtual int foo () = 0;  
};

struct A : public obj
{
    virtual int foo () {std::cout<<"FOO::A"<<std::endl; return 0;} 
};

struct B : public obj
{
    virtual int foo () {std::cout<<"FOO::B"<<std::endl; return 0;} 
};

template<typename T>
struct entryByFile: public T
{
    int entry() 
    {
        return T::make()->foo();
    }
};

struct fileA 
{
    obj* make() {return new A;}
};

struct fileB
{
    obj* make() {return new B;}
};

int
main (void)
{
    return entryByFile<fileB>().entry();
}
Sic luceat lux!
Re[3]: Виртуальный конструктор на шаблонах?
От: Tilir Россия http://tilir.livejournal.com
Дата: 06.08.14 13:38
Оценка:
Здравствуйте, Kernan, Вы писали:

K>>Никак, если имя файла задаётся в рантайм.

K>Если хочется именно в компайлтайм, то что-то вроде этого. Но смысл? то, чего ты хочешь, ты не получишь.
struct obj
{
public:
  virtual int foo () = 0;  
};


Хочется вообще избавиться от виртуальных функций, в чистом виде заменив рантайм-полиморфизм шаблонным. В этом варианте она все таки осталась.
Re[4]: Виртуальный конструктор на шаблонах?
От: Kernan Ниоткуда https://rsdn.ru/forum/flame.politics/
Дата: 06.08.14 13:58
Оценка:
Здравствуйте, Tilir, Вы писали:

T>Хочется вообще избавиться от виртуальных функций, в чистом виде заменив рантайм-полиморфизм шаблонным. В этом варианте она все таки осталась.

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

template<typename T>
struct entryByFile: public T
{
    int entry() 
    {
        return T::foo();
    }
};

struct fileA 
{
    int foo() {std::cout<<"DO something"<<std::endl; return 0;}
};

struct fileB
{
    int foo() {std::cout<<"DO nothing"<<std::endl; return 0;}
};

int main() 
{
    return entryByFile<fileB>().foo();
}
Sic luceat lux!
Re: Виртуальный конструктор на шаблонах?
От: Erop Россия  
Дата: 06.08.14 16:05
Оценка:
Здравствуйте, Tilir, Вы писали:


T>
T>class obj
T>{
T>public:
T>  virtual int foo (int) = 0;    
T>};

T>class A : public obj;
T>class B : public obj;
T>...

T>/* returns A if config have 'a', B is 'b', and so on */
T>obj * 
T>getfromconfig (const char *filename)
T>{
T>  fstream f (filename, ios_base::in);
T>  if (existsin (f, 'a'))
T>    return new A();
T>  if (existsin (f, 'b'))
T>    return new B();
T>  ...
T>}

T>int
T>entry (obj *x)
T>{
T>  return x->foo(); // тут, кситати, не верно метод вызываешь, или неверно описал я  :xz: 
T>}

T>int
T>main (void)
T>{
T>  return entry (getfromconfig("my.xml"))
T>} 
T>


T>Как бы вы переписали этот код с шаблонным полиморфизмом вместо виртуальных функций?



template<typename TObj> int entry( TObj* obj ) { return obj->foo(); }
int
main (void)
{
   const char *filename = "my.xml";
  fstream f (filename, ios_base::in);
  if (existsin (f, 'a'))
    return entry( new A );
  if (existsin (f, 'b'))
    return entry( new B );
  ...
}

Но смысл?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re: Виртуальный конструктор на шаблонах?
От: denisko http://sdeniskos.blogspot.com/
Дата: 06.08.14 18:46
Оценка: 12 (1)
Здравствуйте, Tilir, Вы писали:

Будет некая простыня. Пусть твой фабричный метод возвращает пару целочисленную переменную, задающую тип, и указатель (убыв бы) на сгенерированный тип, отнаследованный от базового. Тогда то, что ты хочешь выглядит примерно так.
(можно короче раза в два но не больше)
struct Base
{
    void foo()
    {
        std::cout << "doNothing" << std::endl;
    }
};
struct A:Base
{
    void foo()
    {
        std::cout << "A" << std::endl;
    }
};
struct B:Base
{
    void foo()
    {
        std::cout << "B" << std::endl;
    }
};
template<typename _Type>
struct RttiEntry
{
    static const int kind = -1; //never hit
    typedef Base Type;
};
template<int kind>
struct InverseRttiEntry
{
    typedef RttiEntry<void> RttiEntryR;
};

template<>
struct RttiEntry<A>
{
    static const int kind = 0xA;
    typedef A Type;
};
template<>
struct RttiEntry<B>
{
    static const int kind = 0xB;
    typedef B Type;
};
template<>
struct InverseRttiEntry<RttiEntry<A>::kind>
{
    typedef RttiEntry<A> RttiEntryR;
};
template<>
struct InverseRttiEntry<RttiEntry<B>::kind>
{
    typedef RttiEntry<B> RttiEntryR;
};

template<int, typename Type>
struct ComparativeCaller:Base{
    void foo(Base* base)
    {
        return base->foo();
    };};
template<>
struct ComparativeCaller<0, A>
{
    void foo(Base* base)
    {
        return ((A*)base)->foo();
    }
};
template<>
struct ComparativeCaller<0, B>
{
    void foo(Base* base)
    {
        return ((B*)base)->foo();
    }
};


template<int kind>
struct Interface:Interface<kind-1>
{
    void foo(int argKind, Base* base)
    {
        if(kind == argKind)
        {
           ComparativeCaller<0, typename InverseRttiEntry<kind>::RttiEntryR::Type>().foo(base);
        }
        else
        {
            Interface<kind-1>::foo(argKind, base);

        }
    }
};
template<>
struct Interface<0>
{
    void foo(int kind, Base* base)
    {
        std::cout << " not implemented " << std::endl;
    }
};


int main() {
    Interface<0xF> rttiInterface;
    A a;
    B b;

    rttiInterface.foo(RttiEntry<A>::kind, &a);
        rttiInterface.foo(RttiEntry<B>::kind, &b);
        rttiInterface.foo(1, &a);

    return 0;}

С моей точки зрения тут надо не выпендриваться и просто сделать развесистый свич, это и проще и читабельнее.
<Подпись удалена модератором>
Re: Виртуальный конструктор на шаблонах?
От: Кодт Россия  
Дата: 07.08.14 11:11
Оценка: 30 (2)
Здравствуйте, Tilir, Вы писали:

T>Как бы вы переписали этот код с шаблонным полиморфизмом вместо виртуальных функций?


Можно вывернуть мехом внутрь, на продолжениях.
struct Entry // чтобы с шаблонами меньше возиться, сделаем дженерик для бедных - конечный тип с полиморфизмом внутри
{
  template<class T> void operator()(T* obj) const { ... }
};

template<class K> void getfromconfig(const char* filename, K kont)
{
  .....
  kont(new A());
  .....
  kont(new B());
  .....
}

int main()
{
  getfromconfig(filename, Entry());
}


Общая схема рефакторинга:
PolymorphResult foo(Args...);
void foo(Args..., PolymorphResult& res);
void foo(Args..., function<PolymporphResult&> kont);
void foo(Args..., PolymorphFunction kont);


А чтоб удобнее пользоваться было, а не громоздить руками — придётся прикручивать карринг и комбинаторную логику.
Перекуём баги на фичи!
Re[2]: Виртуальный конструктор на шаблонах?
От: Tilir Россия http://tilir.livejournal.com
Дата: 07.08.14 11:23
Оценка:
Здравствуйте, Erop, Вы писали:

E>Но смысл?


Это читерство. Идея контрпримера была в том, что getfromconfig используется не только в entry, а где угодно.
Re[2]: Виртуальный конструктор на шаблонах?
От: Tilir Россия http://tilir.livejournal.com
Дата: 07.08.14 11:30
Оценка:
Здравствуйте, denisko, Вы писали:

D>Будет некая простыня. Пусть твой фабричный метод возвращает пару целочисленную переменную, задающую тип, и указатель (убыв бы) на сгенерированный тип, отнаследованный от базового. Тогда то, что ты хочешь выглядит примерно так.


Очень круто, я серьёзно. К сожалению при всей методологической полезности, организация RTTI руками в этом случае как раз и доказывает необходимость именно динамического полиморфизма.
Re[2]: Виртуальный конструктор на шаблонах?
От: Tilir Россия http://tilir.livejournal.com
Дата: 07.08.14 12:42
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Можно вывернуть мехом внутрь, на продолжениях.


Очень красивый вариант.

Но не будет ли в этом случае взрыва при расширении, скажем на два-три-N полиморфных параметров?

Мне сходу не ясно как красиво переписать вот такое:

int entry (obj *, obj *);
...
entry (getfromconfig("my.xml"), getfromconfig("his.xml"))


К>А чтоб удобнее пользоваться было, а не громоздить руками — придётся прикручивать карринг и комбинаторную логику.


Было бы очень интересно посмотреть хотя бы набросок.
Re[4]: Виртуальный конструктор на шаблонах?
От: F3V  
Дата: 07.08.14 13:17
Оценка: 8 (1)
Здравствуйте, Tilir, Вы писали:

T>Хочется вообще избавиться от виртуальных функций, в чистом виде заменив рантайм-полиморфизм шаблонным.

Может я чего не так понял, но предположу вариант:
  Скрытый текст
#include <iostream>

template<typename Tnext>
class Converter
{
public:
    template<typename Tthis>
    inline static Tnext* cast(Tthis* _this)
    {
        return static_cast<Tnext*>(_this);
    }
};

template<typename Tfist, typename Tsecond>
class ChainConverter
{
public:
    template<typename Tthis>
    inline static auto cast(Tthis* _this)-> decltype (Tsecond::cast(Tfist::cast(_this)))
    {
        return Tsecond::cast(Tfist::cast(_this));
    }
};

template<typename TConverter>
class obj
{
public:
    typedef obj<TConverter> TObj;

public:
    // предположим:
    /*virtual*/ int foo() /* = 0;*/
    { return TConverter::cast(this)->foo(); }

    // чтобы сделать виртуальный вызов из наследников без приведения
    int virtualFoo(){ return foo(); }

protected:
    obj(){}
};

template<typename TConverter>
class Aimpl : public obj<ChainConverter<Converter<Aimpl<TConverter>>,TConverter>>
{
public:
    typedef Aimpl<TConverter> TAimpl;

public:
    int foo() {std::cout << "FOO::A" << std::endl; return 0; }
};

template<typename TConverter>
class Bimpl : public Aimpl<ChainConverter<Converter<Bimpl<TConverter>>, TConverter>>
{
public:
    typedef Bimpl<TConverter> TBimpl;

public:
    int foo() { std::cout << "FOO::B" << std::endl; return 0; }
};

template<typename TConverter>
class Cimpl : public Aimpl<ChainConverter<Converter<Cimpl<TConverter>>, TConverter>>
{
public:
    typedef Cimpl<TConverter> TCimpl;

public:
    int foo() { std::cout << "FOO::C" << std::endl; return 0; }
};

template<typename TConverter>
class Dimpl :
    public Bimpl<ChainConverter<Converter<Dimpl<TConverter>>, TConverter>>,
    public Cimpl<ChainConverter<Converter<Dimpl<TConverter>>, TConverter>>
{
public:
    typedef Dimpl<TConverter> TDimpl;

public:
    int foo() { std::cout << "FOO::D" << std::endl; return 0; }
};

class A : public Aimpl<Converter<A>>
{
};

class B : public Bimpl<Converter<B>>
{
};

class C : public Cimpl<Converter<C>>
{
};

class D : public Dimpl<Converter<D>>
{
};

int main()
{
    A a;
    a.foo();
    a.virtualFoo();

    B b;
    B::TObj& ob = b;
    B::TAimpl& aimpl = b;

    ob.foo();
    ob.virtualFoo();

    b.foo();
    b.virtualFoo();

    aimpl.foo();
    aimpl.virtualFoo();

    std::cout << "--D--" << std::endl;

    D d;
    d.foo();
    d.TBimpl::foo();
    d.TBimpl::TAimpl::foo();
    d.TCimpl::foo();
    d.TCimpl::TAimpl::foo();
    d.TBimpl::TObj::virtualFoo();
    d.TCimpl::TObj::virtualFoo();

    return 0;
}
/* Output:
FOO::A
FOO::A
FOO::B
FOO::B
FOO::B
FOO::B
FOO::A
FOO::B
--D--
FOO::D
FOO::B
FOO::A
FOO::C
FOO::A
FOO::D
FOO::D
// */
Re[5]: Виртуальный конструктор на шаблонах?
От: Tilir Россия http://tilir.livejournal.com
Дата: 07.08.14 13:34
Оценка:
Здравствуйте, F3V, Вы писали:

F3V>Может я чего не так понял, но предположу вариант:


Тоже хороший вариант. Известна двойственность виртуальных функций и PImpl. Но по сути это такое же "RTTI своими силами", как и в случае http://rsdn.ru/forum/cpp/5728786.1
Автор: denisko
Дата: 06.08.14

Такой подход скорее доказывает что динамический полиморфизм не сводится к шаблонному, раз уже нам пришлось по факту его воспроизводить, заводя ту же vtbl но руками.

Хочется же чего-то вроде: http://rsdn.ru/forum/cpp/5729965.1
Автор: Кодт
Дата: 07.08.14
т.е. использование мощностей шаблонного полиморфизма в чистом виде.
Re[3]: Виртуальный конструктор на шаблонах?
От: Кодт Россия  
Дата: 07.08.14 14:25
Оценка:
Здравствуйте, Tilir, Вы писали:

T>Но не будет ли в этом случае взрыва при расширении, скажем на два-три-N полиморфных параметров?


Разумеется, будет. Сколько существует сочетаний типов, столько будет воплощений.

T>Мне сходу не ясно как красиво переписать вот такое:


T>
T>int entry (obj *, obj *);
T>...
T>entry (getfromconfig("my.xml"), getfromconfig("his.xml"))
T>


На полиморфных лямбдах это будет так
int res;
getfromconfig("my.xml", [&](auto my) {
  getfromconfig("his.xml", [&](auto his) {
    res = entry(my,his);
  })
});


К>>А чтоб удобнее пользоваться было, а не громоздить руками — придётся прикручивать карринг и комбинаторную логику.


T>Было бы очень интересно посмотреть хотя бы набросок.


Чуть попозже попробую формально подойти к этому.
Кажется, что std::bind на такое, в принципе, способен.
Перекуём баги на фичи!
Re[6]: Виртуальный конструктор на шаблонах?
От: F3V  
Дата: 08.08.14 10:09
Оценка: 8 (1)
Здравствуйте, Tilir, Вы писали:

T>Хочется же чего-то вроде: http://rsdn.ru/forum/cpp/5729965.1
Автор: Кодт
Дата: 07.08.14
т.е. использование мощностей шаблонного полиморфизма в чистом виде.

Кодт решил задачку: http://www.rsdn.ru/forum/cpp/5730329.1
Автор: Кодт
Дата: 07.08.14
.
Если я его правильно понял, то он предложил перенести точку входа в программу во внутреннюю лямбду, где конфигурация уже определена.
Тогда не код будет управлять данными, а под данные будет подстраиваться код...
Если немного переделать, то получится примерно так:
  Скрытый текст
#include <iostream>
#include <string>
#include <type_traits>

class A
{
public:
    int foo() { std::cout << "FOO::A" << std::endl; return 0; }
};

class B
{
public:
    int foo() { std::cout << "FOO::B" << std::endl; return 0; }
};

class C
{
public:
    int foo() { std::cout << "FOO::C" << std::endl; return 0; }
};

class D
{
public:
    int foo() { std::cout << "FOO::D" << std::endl; return 0; }
};

template<int AppRestartRequestCount>
class EmptyConfig
{
public:
    static const int typesRemainsCount = 4;
    static const int appRestartRequestCount = AppRestartRequestCount;
};

template<typename TParentConfig, typename T, typename Q = T>
class Config :public TParentConfig
{
public:
    typedef T TType;
    typedef TParentConfig TParent;

public:
    static const int typesRemainsCount = TParentConfig::typesRemainsCount - 1;
};

template<typename TParentConfig, typename T>
class Config<TParentConfig, T, typename std::enable_if<TParentConfig::typesRemainsCount == 0, T>::type>
{
public:
    typedef T TType;
    typedef TParentConfig TParent;

public:
    static const int typesRemainsCount = -1;
};

template<typename TLayeredConfig>
class FullConfig
{
public:
    typedef typename TLayeredConfig::TType T4;
    typedef typename TLayeredConfig::TParent::TType T3;
    typedef typename TLayeredConfig::TParent::TParent::TType T2;
    typedef typename TLayeredConfig::TParent::TParent::TParent::TType T1;
    
public:
    static const int typesPackCount = EmptyConfig<0>::typesRemainsCount;
    static const int appRestartRequestCount = TLayeredConfig::appRestartRequestCount;
};

template<typename TConfig>
class ReverseConfig
{
public:
    typedef typename TConfig::T1 T4;
    typedef typename TConfig::T2 T3;
    typedef typename TConfig::T3 T2;
    typedef typename TConfig::T4 T1;

    static const int typesPackCount = TConfig::typesPackCount;
    static const int appRestartRequestCount = TConfig::appRestartRequestCount - 1;
};

template<typename TConf>
class AppStop
{
public:
    static int run()
    {
        return -1;
    }
};

template<typename TConf>
class App
{
public:
    static int run()
    {
        typename TConf::T1 t1;
        typename TConf::T2 t2;
        typename TConf::T3 t3;
        typename TConf::T4 t4;

        t1.foo();
        t2.foo();
        t3.foo();
        t4.foo();

        std::cout << "reverse reconfig.." << std::endl;

        return std::conditional<TConf::appRestartRequestCount != 0, App<ReverseConfig<TConf>>, AppStop<TConf>>::type::run();
    }
};

template<typename TConfig, template <typename> class TApp, typename Q = TConfig>
class Configurator
{
public:
    static int configAndRun(const std::string& /*_s*/)
    {
        return TApp<FullConfig<TConfig>>::run();
    }
};

template<typename TConfig, template <typename> class TApp>
class Configurator<TConfig, TApp,
    typename std::enable_if<TConfig::typesRemainsCount != 0, TConfig>::type>
{
public:
    static int configAndRun(const std::string& _s)
    {
        auto c = _s.at(0);
        auto nextS = _s.substr(1);
        switch (c)
        {
        default:
        case 'A':
            return Configurator<Config<TConfig, A>, TApp>::configAndRun(nextS);
        case 'B':
            return Configurator<Config<TConfig, B>, TApp>::configAndRun(nextS);
        case 'C':
            return Configurator<Config<TConfig, C>, TApp>::configAndRun(nextS);
        case 'D':
            return Configurator<Config<TConfig, D>, TApp>::configAndRun(nextS);
        }
    }
};

int main()
{
    const char* configs[] = { "ABCD", "DABC" };
    for (auto& cfg : configs)
    {
        std::cout << "main config: " << cfg << std::endl;
        Configurator<EmptyConfig<1>, App>::configAndRun(cfg);
    }
    return 0;
}
/* Output:
main config: ABCD
FOO::A
FOO::B
FOO::C
FOO::D
reverse reconfig..
FOO::D
FOO::C
FOO::B
FOO::A
reverse reconfig..
main config: DABC
FOO::D
FOO::A
FOO::B
FOO::C
reverse reconfig..
FOO::C
FOO::B
FOO::A
FOO::D
reverse reconfig..
*/
Re[3]: Виртуальный конструктор на шаблонах?
От: Erop Россия  
Дата: 08.08.14 11:05
Оценка:
Здравствуйте, Tilir, Вы писали:

T>Это читерство.

Это не читерство, а статический полиморфизм так работает...


T>Идея контрпримера была в том, что getfromconfig используется не только в entry, а где угодно.

Ну так и пишешь это всё "где угодно" шаблонным, потом параметризуешь так и сяк, а вместо конфига можно просо нужную версию ставить, например, или в мэйне выбирать...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re: Виртуальный конструктор на шаблонах?
От: Кодт Россия  
Дата: 11.08.14 09:53
Оценка: 12 (1)
Здравствуйте, Tilir, Вы писали:

Вот, накидал более-менее изящную демонстрацию
#include <cstdio>
#include <vector>

#include <functional>
using namespace std;
using namespace std::placeholders;


// семейство типов ABCD

void show(char x) { printf("%c ", x); }

struct A { int      foo() { show('A'); return 1; } } const a;
struct B { unsigned foo() { show('B'); return 2; } } const b;
struct C { long     foo() { show('C'); return 3; } } const c;
struct D { double   foo() { show('D'); return 4; } } const d;

// все наши полиморфные функции будут иметь первоклассный фиксированный тип

struct getconfig_t // void g(char,K)
{
    template<class K> // где K имеет сигнатуру void f(ABCD)
    void operator()(char t, K kont) const
    {
        switch(t)
        {
        case 'a': kont(A()); break;
        case 'b': kont(B()); break;
        case 'c': kont(C()); break;
        case 'd': kont(D()); break;
        default:  printf("exception %c\n", t); break; // исключение состоит в том, чтобы не вызывать продолжение :)
        }
    }
} const getconfig;

struct entry_t // void e(ABCD,ABCD,K)
{
  template<class T1, class T2, class K> // где T1 и T2 это ABCD, а K имеет сигнатуру void f(NUM)
  void operator()(T1 t1, T2 t2, K kont) const
  {
    kont(t1.foo()*10 + t2.foo()); // будем изучать правила продвижения числовых типов
  }
} const entry;

struct finish_t // void f(NUM)
{
    void operator()(int           n) const { printf("int    %d\n",  n); }
    void operator()(unsigned      n) const { printf("uint   %u\n",  n); }
    void operator()(long          n) const { printf("long   %ld\n", n); }
    void operator()(unsigned long n) const { printf("ulong  %lu\n", n); }
    void operator()(double        n) const { printf("double %g\n",  n); }
} const finish;



// протектор, аналогичный boost::protect
// (его нет в стандартной библиотеке, поэтому пришлось стырить из интернета рецепт)

template<typename T>
struct protect_wrapper : T
{
    protect_wrapper(const T& t) : T(t)
    {

    }

    protect_wrapper(T&& t) : T(std::move(t))
    {

    }
};

template<typename T>
typename std::enable_if< !std::is_bind_expression< typename std::decay<T>::type >::value,
                T&& >::type
protect(T&& t)
{
    return std::forward<T>(t);
}

// нам потребуется только эта ветка
template<typename T>
typename std::enable_if< std::is_bind_expression< typename std::decay<T>::type >::value,
                protect_wrapper<typename std::decay<T>::type > >::type
protect(T&& t)
{
    return protect_wrapper<typename std::decay<T>::type >(std::forward<T>(t));
}

// устойчивая идиома: изоляция bind-выражения

template<class... Args>
auto probind(Args... args) -> decltype(protect(bind(args...))) { return protect(bind(args...)); }



// карринг двухместной функции

struct curry_t
{
    template<class F, class X>
    auto operator()(F&& f, X&& x) const -> decltype( probind(f,x,_1) ) { return probind(f,x,_1); }
} const curry;

// комбинируем!

void run(char x, char y)
{
    // кирпичики: продолжения
    auto g = protect(bind(getconfig, x, _1));  // void(K)
    auto h = protect(bind(getconfig, y, _1));  // void(K)
    auto e = protect(bind(entry, _1, _2, _3)); // void(ABCD,ABCD,K)
    // последний кирпичик - без продолжения
    auto f = finish;

    // комбинации
    auto fe   = probind(e,_1,_2,f);
    auto feh  = probind(h, bind(curry,fe,_1));
    auto fegh = probind(g, feh);

    // запускаем
    fegh();
}

int main()
{
    vector<char> abcd = { 'a','b','c','d','e' };
    for(auto x : abcd)
        for(auto y : abcd)
            run(x,y);
}

Карринг здесь нужен для того, чтобы разнести первый и второй аргументы замыкания fe на разные этажи bind-выражения.
Если просто написать probind(h, bind(fe,_1,_2)) — то получим двухместное замыкание, в котором h получает на вход конечный результат fe(_1,_2)
Если вместо bind сделать probind(h, probind(fe,_1,_2)) — то получим нульместное замыкание, в котором h получает двухместное.
А нам нужно одноместное и одноместное.
В стандартной библиотеке есть комбинатор bind1st, но, к сожалению, это шаблон функции.
Перекуём баги на фичи!
Re[2]: Виртуальный конструктор на шаблонах?
От: Кодт Россия  
Дата: 11.08.14 16:04
Оценка:
К>
К>// карринг двухместной функции

К>struct curry_t
К>{
К>    template<class F, class X>
К>    auto operator()(F&& f, X&& x) const -> decltype( probind(f,x,_1) ) { return probind(f,x,_1); }
К>} const curry;

// более универсальное решение - расслаивание аргументов на немедленно подставленные и идущие в составе синтаксического дерева bind

struct _1_t {} const _1_;  auto _pass_(_1_t) -> decltype(_1) { return _1; }
struct _2_t {} const _2_;  auto _pass_(_2_t) -> decltype(_2) { return _2; }
struct _3_t {} const _3_;  auto _pass_(_3_t) -> decltype(_3) { return _3; }
template<class T> auto _pass_(T&& t) -> T&& { return t; }

struct partial_t
{
    template<class F>
    auto operator()(F&& f) const -> decltype( probind(f) )
    { return probind(f); }

    template<class F, class T1>
    auto operator()(F&& f, T1&& t1) const -> decltype( probind(f, _pass_(t1)) )
    { return probind(f, _pass_(t1)); }

    template<class F, class T1, class T2>
    auto operator()(F&& f, T1&& t1, T2&& t2) const -> decltype( probind(f, _pass_(t1), _pass_(t2)) )
    { return probind(f, _pass_(t1), _pass_(t2)); }
} const partial;



К>// комбинируем!

К>void run(char x, char y)
К>{
К>    // кирпичики: продолжения
К>    auto g = protect(bind(getconfig, x, _1));  // void(K)
К>    auto h = protect(bind(getconfig, y, _1));  // void(K)
К>    auto e = protect(bind(entry, _1, _2, _3)); // void(ABCD,ABCD,K)
К>    // последний кирпичик - без продолжения
К>    auto f = finish;

К>    // комбинации
К>    auto fe   = probind(e,_1,_2,f);
      auto feh  = probind(h, bind(partial,fe, _1_, _1));
К>    auto fegh = probind(g, feh);

К>    // запускаем
К>    fegh();
К>}
К>


Здесь _1 является формальным аргументом замыкания feh и будет подставлен при вызове его из h
а _1_ — аргументом того замыкания, которое будет возвращено функцией partial.
Перекуём баги на фичи!
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.