Как использовать списки типов - PullRequest
11 голосов
/ 23 мая 2009

Я читал о списках типов в «Современном C ++ Design» и понял, что это своего рода объединение типов. Помещая разные несвязанные типы в список типов, можно использовать его для одновременного представления нескольких типов без наследования. Я тестировал список типов в некоторых простых функциях с примитивными типами, но я не мог заставить их работать.

Может ли кто-нибудь сказать мне, правильно ли мое понимание списков типов, и привести простой реальный пример того, как использовать списки типов в среднем за день коде? Заранее спасибо.

Кстати, я использую Windows и Visual Studio 2005 и ее компилятор.

РЕДАКТИРОВАТЬ: мои примеры пропали, я использую проект песочницы в vs, чтобы проверить эти вещи. Но это было тихо похоже на код из учебника Доббса:

void SomeOperation(DocumentItem* p)
{
    if (TextArea* pTextArea = dynamic_cast<TextArea*>(p))
    {
        ... operate on a TextArea object ...
    }
    else if (VectorGraphics* pVectorGraphics =
        dynamic_cast<VectorGraphics*>(p))
    {
        ... operate on a VectorGraphics object ...
    }
    else if (Bitmap* pBitmap = dynamic_cast<Bitmap*>(p))
    {
        ... operate on a Bitmap object ...
    }
    else
    {
        throw "Unknown type passed";
    }
}

Это работает, но я не вижу преимущества перед наследованием, которое способно сделать то же самое. И динамическое приведение не работает на примитивных типах. Можно ли использовать его как возвращаемое значение, например:

typedef Typelist<int, string> mylist
mylist myfunction() {
    if(foo == bar)
        return 5;

    return "five";
}

Ответы [ 2 ]

19 голосов
/ 24 мая 2009

Списки типов являются общими коллекциями типов во время компиляции. Если вы используете dynamic_cast, вы упускаете точку, потому что она не нужна, потому что это статическая концепция времени компиляции.

Это работает, но я не вижу преимущества перед наследованием, которое способно сделать то же самое.

Вы не можете заставить любой существующий тип наследовать от чего угодно. Это просто невозможно, поскольку этот существующий тип может быть встроенным типом или типом из библиотеки. Думайте о списках типов как о расширениях списков типов (например, в std :: pair) для любого разумного количества типов (вместо 2).

Списки типов могут использоваться, чтобы создать средство для передачи набора аргументов функции. Это фрагмент кода, который вызывает обобщенные функторы из 5 параметров (еще один концепт от Modern C ++ design) с аргументами, предоставленными в tupe (еще одном) со списком типов, который определяет типы объектов, содержащихся в кортеже:

//functor is just a holder of a pointer to method and a pointer to object to call this 
//method on; (in case you are unfamiliar with a concept)
template<class R, class t0, class t1, class t2, class t3, class t4>
R call(Loki::Functor<R,LOKI_TYPELIST_5(t0, t1, t2, t3, t4
    )> func,
    Loki::Tuple<LOKI_TYPELIST_5(t0, t1, t2, t3, t4)> tuple)
{
    ///note how you access fields
    return func(Loki::Field<0>(tuple), Loki::Field<1>(tuple),
        Loki::Field<2>(tuple), Loki::Field<3>(tuple),
        Loki::Field<4>(tuple));
}

//this uses the example code
#include<iostream>
using namespace std;

int foo(ostream* c,int h,float z, string s,int g)
{
    (*c)<<h<<z<<s<<g<<endl;
    return h+1
}

int main(int argc,char**argv)
{
    Loki::Functor<int,LOKI_TYPELIST_5(ostream*, int, float, string, int)> f=foo;
    //(...)
    //pass functor f around
    //(...)
    //create a set of arguments
    Loki::Tuple<LOKI_TYPELIST_5(ostream*, int, float, string, int)> tu;
    Field<0>(tu)=&cout;
    Field<1>(tu)=5;
    Field<2>(tu)=0.9;
    Field<3>(tu)=string("blahblah");
    Field<4>(tu)=77;
    //(...)
    //pass tuple tu around, possibly save it in a data structure or make many 
    //specialized copies of it, or just create a memento of a call, such that 
    //you can make "undo" in your application; note that without the typelist 
    //you would need to create a struct type to store any set of arguments;
    //(...)
    //call functor f with the tuple tu
    call(f,tu);
}

Обратите внимание, что только с другими понятиями, такими как кортежи или функторы, списки типов становятся полезными. Кроме того, я испытываю Loki около 2-х лет в проекте, и из-за кода шаблона (во многом его) размеры исполняемых файлов в версиях DEBUG, как правило, БОЛЬШИЕ (моя запись была 35 МБ или около того). Также был небольшой удар по скорости компиляции. Также напомним, что C ++ 0x, вероятно, будет включать некоторый эквивалентный механизм. Вывод: старайтесь не использовать списки типов, если это не нужно.

4 голосов
/ 23 мая 2009

Списки типов - это способ передачи «списков параметров» шаблонным метапрограммам, которые «выполняются» как часть процесса компиляции.

Как таковые, они могут быть использованы для генерации какого-то типа "объединения", но это только одно возможное использование.

Для примера из "реального мира": мы использовали списки типов как способ автоматической генерации метода "QueryInterface" при реализации COM-объектов в библиотеке Comet .

Это позволило вам написать код так:

class Dog : public implement_qi<make_list<IAnimal, INoisy, IPersistStream> >
{
    // The implement_qi template has provided
    // an implementation of COM's QueryInterface method for us without
    // having to write an ugly ATL "message map" or use any Macros.
    ...
}

В этом примере «make_list» был шаблоном, используемым для генерации «списка типов», который шаблон Implement_qi мог бы затем «перечислить» до , генерируя соответствующий код QueryInterface .

...