А как "восстанавливать" информацию о типах? Обратный type erasure
От: x-code  
Дата: 17.02.15 08:26
Оценка:
Попробую объяснить что мне нужно (хотя это и непросто)

Есть код, написанный на шаблонах. Выбор специализаций происходит на этапе компиляции.
Далее, в программе где-то происходит "type erasure", информация о типах для компилятора теряется.

Например, есть некая структура.
И есть некая унифицированная property table, таблица из двух столбцов (имя и значение) и N строк (каждая строка — поле структуры).
В какой-то момент эта таблица заполняется унифицированным способом. В ней есть имена полей и их значения. Каждой строке как-бы соответствует какой-то тип данных. Поскольку сама таблица унифицированная, в ней уже нет compile-time информации о типах полей; но в ней могут быть некие runtime идентификаторы этих типов (скажем, элементы enum).

Дальше, пользователь что-то щелкает мышью, и нам нужно произвести обратное действие — восстановить из строки тип, опять перейти к шаблонному коду. Например, по щелчку на некотором поле нужно узнать что там был за тип и вызвать шаблонную функцию, специализированную для этого типа, которая что-то сделает.

То есть, в общем и целом — нужна какая-то конструкция типа большого switch, которая бы позволяла явно вызвать правильную специализацию шаблонного кода в зависимости от значения некой переменной времени выполнения.

Иными словами, как называется операция, обратная type erasure, как это обычно делают и делают ли вообще? Какие идиомы существуют для этого?
Re: А как "восстанавливать" информацию о типах? Обратный type erasure
От: uzhas Ниоткуда  
Дата: 17.02.15 08:38
Оценка: 2 (1) +3
Здравствуйте, x-code, Вы писали:

XC>Например, есть некая структура.

XC>И есть некая унифицированная property table, таблица из двух столбцов (имя и значение) и N строк (каждая строка — поле структуры).
XC>В какой-то момент эта таблица заполняется унифицированным способом. В ней есть имена полей и их значения. Каждой строке как-бы соответствует какой-то тип данных. Поскольку сама таблица унифицированная, в ней уже нет compile-time информации о типах полей; но в ней могут быть некие runtime идентификаторы этих типов (скажем, элементы enum).

XC>Дальше, пользователь что-то щелкает мышью, и нам нужно произвести обратное действие — восстановить из строки тип, опять перейти к шаблонному коду. Например, по щелчку на некотором поле нужно узнать что там был за тип и вызвать шаблонную функцию, специализированную для этого типа, которая что-то сделает.


есть такой паттерн: variant
в бусте есть. можно и switch использовать и визитор
Re: А как "восстанавливать" информацию о типах? Обратный type erasure
От: BulatZiganshin  
Дата: 17.02.15 11:07
Оценка:
Здравствуйте, x-code, Вы писали:

XC>То есть, в общем и целом — нужна какая-то конструкция типа большого switch, которая бы позволяла явно вызвать правильную специализацию шаблонного кода в зависимости от значения некой переменной времени выполнения.


http://en.cppreference.com/w/cpp/language/typeid
Люди, я люблю вас! Будьте бдительны!!!
Re: А как "восстанавливать" информацию о типах? Обратный type erasure
От: Кодт Россия  
Дата: 17.02.15 11:24
Оценка:
Здравствуйте, x-code, Вы писали:

XC>Иными словами, как называется операция, обратная type erasure, как это обычно делают и делают ли вообще? Какие идиомы существуют для этого?


Полиморфизм времени исполнения это называется. И мультиметоды.

1. Если есть некоторый шаблон функции,
template<class T> void foo(T x)
{
  ...
  bar(x);
  buz(x);
  xyz<T>(); // не зависит от наличия x
  ...
  T y; // не зависит от наличия x
  T z(x);
  ...
}

то его можно перетащить в рантайм следующими способами
— ограничить T классами, сделать старое доброе ООП
void foo(IFoo const& x)
{
  ...
  x->bar();
  buz(x);
  xyz(x); // вынужденно зависит от наличия x
  ...
  shared_ptr<IFoo> y(x->Create()); // вынужденно зависит от наличия x
  z(x->Clone());
  ...
}

— вынести функции работы с типом наружу, сделать словарь методов
void foo(void* x, IFooTraits const* xtraits)
{
  ...
  xtraits->bar(x);
  buz(x, xtraits);
  xyz(xtraits); // словарь можно передавать отдельно
  ...
  shared_ptr<void> y = xtraits->Create(); // не зависит от наличия x
  shared_ptr<void> z = xtraits->Clone(x);
  ...
}


2. Специализации шаблонов и перегрузки функций — если они не расползаются — можно организовать через переопределение словаря методов
template<class T> void foo(T);
template<class E> void foo(vector<E>);
void foo(int);

template<class T> void bar() { T x; foo(x); }

превращается в
struct IBarTraits
{
  virtual shared_ptr<void> Create() const = 0;
  virtual void foo(shared_ptr<void> x) const = 0;
};

template<class T>
struct GeneralBarTraits : IBarTraits { ..... };

template<class E>
struct VectorBarTraits : IBarTraits { ..... };

struct IntBarTraits : IBarTraits { ..... };

void bar(IBarTraits* traits) { shared_ptr<void> x = traits->Create(); traits->foo(x); }


3. Если таких семейств функций много, и они расползаются, можно пойти дальше: сделать множественное наследование типов и трейтсов, dynamic_cast'ить их к нужным интерфейсам.
struct ITraits { virtual ~ITraits() {} }; // просто корень иерархии с виртуальными функциями

struct ICtorTraits : ITraits { ..... };
struct IFooTratis : ITraits { ..... };
struct IBarTraits : ITraits { ..... };
.....

void foo(ITraits& t)
{
  shared_ptr<void> x = dynamic_cast<ICtorTraits&>(t).Create();
  dynamic_cast<IFooTraits&>(t).foo_nullary();
  dynamic_cast<IFooTraits&>(t).foo_unary(x);
}


4. Подводя итоги, могу сказать следующее. Всё это добро уже давно изобретено. Посмотри на реализации COM (ATL, Comet) и на boost::any.
То, что я написал выше, — это просто для понимания, как оно устроено. В COM трейтсам соответствуют фабрики классов, например.
Перекуём баги на фичи!
Re: А как "восстанавливать" информацию о типах? Обратный type erasure
От: Evgeny.Panasyuk Россия  
Дата: 17.02.15 11:27
Оценка:
Здравствуйте, x-code, Вы писали:

XC>Дальше, пользователь что-то щелкает мышью, и нам нужно произвести обратное действие — восстановить из строки тип, опять перейти к шаблонному коду. Например, по щелчку на некотором поле нужно узнать что там был за тип и вызвать шаблонную функцию, специализированную для этого типа, которая что-то сделает.


Помимо variant'а (который нужно рассматривать в первую очередь) можно сделать такое type-erasure, которое будет запоминать фиксированный набор операций для значений стираемых типов. Например на базе Boost.TypeErasure:
LIVE DEMO
BOOST_TYPE_ERASURE_FREE((has_action), action, 1)

using any_with_action = any<mpl::vector<copy_constructible<>, has_action<void(_self&)>>>;

struct Foo{};
struct Bar{};

void action(Foo&)
{
    cout << "Foo" << endl;
}

void action(Bar&)
{
    cout << "Bar" << endl;
}

int main()
{
    any_with_action x = Foo{}, y = Bar{};
    action(x); // prints "Foo"
    action(y); // prints "Bar"
}
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.