мета-программирование безумия с ++ - PullRequest
2 голосов
/ 23 октября 2010

рассмотрим следующие шаблонные структуры данных

enum eContent{
    EINT = 1,
    EFLOAT = 2,
    EBOOL = 4
};

template<int>
struct Container{
    Container(){assert(false);} //woops, don't do that!
};

template<>
struct Container<EINT>{
    Container():i(123){}
    int i;
};

template<>
struct Container<EFLOAT>{
    Container():f(123.456f){}
    float f;
};

template<>
struct Container<EBOOL>{
    Container():b(true){}
    bool b;
};



<fancy macro goes here that creates me all kind of combinations including for example>
    template<>
    struct Container<EFLOAT | EBOOL>: public Container<EFLOAT>, public Container<EBOOL>{
        Container():Container<EFLOAT>(),Container<EBOOL>(){}
    };
</fancy macro>

такой, что я могу, например, определить переменную следующим образом:

Container<EINT|EFLOAT|EBOOL> myVar;

как бы я определил этот причудливый макрос?

Почему я этого хочу? Пусть это будет ради развлечения и изучения метапрограммирования

Ответы [ 4 ]

3 голосов
/ 23 октября 2010

Ну, во-первых, || - логическое значение или оператор;при использовании так, как вы его использовали, это всегда будет приводить к 1 (или, скорее, true, но true всегда повышается до 1 при приведении к int, как в данном случае), чтов случае, если ваш код равен EINT, поэтому ваш шаблон всегда будет создаваться как Container<EINT>.

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

Точно что вы пытаетесь достичь?Существуют способы сделать тип достаточно гибким, чтобы хранить несколько данных нескольких типов, но оператор or не делает ничего, что вы хотите, в контексте аргументов шаблона.

1 голос
/ 23 октября 2010
enum eContent{
    eInt    = 1,
    eFloat  = 2,
    eBool   = 4
};

template<unsigned, unsigned>
struct Member {};

template<>
struct Member<eInt, eInt>{
    Member():i(123){}
    unsigned i;
};

template<>
struct Member<eFloat, eFloat>{
    Member():f(123.456f){}
    float f;
};

template<>
struct Member<eBool, eBool>{
    Member():b(true){}
    bool b;
};

template< unsigned members >
struct Container
    : Member< members & eInt, eInt >
    , Member< members & eFloat, eFloat >
    , Member< members & eBool, eBool >
{};

int main()
{
    Container< eFloat | eBool > c;
    c.f;    // OK
    c.b;    // OK
    c.i;    // !Nah
}

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

Если у вас есть какая-то реальная проблема (для которой вы думаете, что это может быть решением), попробуйте спросить об этом.

Если это не просто игра или домашнее задание, конечно. : -)

Приветствия & hth.,

PS: В соответствии с хорошей практикой программирования на C ++, резервируйте ВСЕ ВЕРХНИЕ имена для макросов и только для макросов. Таким образом вы избежите многих потенциальных конфликтов имен. Использование ALL UPPERCASE для констант является Java / Python / и т. Д. соглашение, в некоторой степени, подходит для этих языков, но определенно не для C ++. Это происходит из раннего C, где константы должны были быть выражены как макросы. ALL UPPERCASE использовался (и есть) для макросов, а не для констант - ну, кроме Брайана Кернигана, но давайте не будем углубляться в историю ...; -)

0 голосов
/ 23 октября 2010

хорошо, я использую ваши Контейнерные структуры, объединяю их с XCont и определяю XContainer, который вы хотите:

// a (bit-)LIST is an int that contains the value (TAIL<<1|HEAD),
// where TAIL is a LIST, and HEAD is either 1 or 0.
// while iterating through the LIST from right,
// SHL counts how far each consumed HEAD has to be shifted left,
// back to its original position.

template<int TAIL,int HEAD,int SHL>
struct XCont;

//the empty LIST
template<int SHL>
struct XCont<0,0,SHL>{};

//HEAD equals 0, so we just recurse through the TAIL.
template<int TAIL,int SHL>
struct XCont<TAIL,0,SHL>:public XCont< (TAIL>>1) , (TAIL&1) , (SHL+1) >{};

//HEAD equals 1, so we do like above, but we have to append Container< (1<<SHL) >.
template<int TAIL,int SHL>
struct XCont<TAIL,1,SHL>:public XCont< (TAIL>>1) , (TAIL&1) , (SHL+1) >,public Container< (1<<SHL) >{};


template<int E>
struct XContainer:public XCont< (E>>1) , (E&1) , (0) >{};

Работает так:

  • Битовая маска будет интерпретироваться как СПИСОК битов справа налево (сначала младший бит).
  • Мы перебираем биты, сдвигая биты (>>) LIST.
  • СПИСОК представлен в стиле функционального программирования в виде кортежа HEAD и TAIL, где HEAD - это первый элемент, а TAIL - это список оставшихся элементов без этого HEAD.
  • Всякий раз, когда мы находим 1-бит в качестве HEAD, мы хотим пересчитать его битовую позицию, поэтому мы считаем это с помощью SHL. Конечно, есть и другие подходы, такие как битовое смещение маски над списком и проверка ее значения на 0 и не на 0.

Они равны:

  • XContainer
  • XContainer <0x04 | 0x02 | 0x01>
  • XContainer <0x07>
  • XCont <(0x07 >> 1), (0x07 & 1), (0)>
  • XCont <((EBOOL | EFLOAT | EINT) >> 1), (EINT), (0)>
  • XCont <((EBOOL | EFLOAT) >> 1), (EINT), (0)>
  • XCont <((EBOOL | EFLOAT) >> 1), (EINT >> 0), (0)>
  • XCont , где ...
    • TAIL = ((EBOOL | EFLOAT) >> 1)
    • 1 = (EINT >> 0)
    • SHL = (0)
    • EINT = (1 << SHL) </em>
  • XCont > 1, TAIL & 1, SHL + 1> ++ Контейнер <(1 << SHL)>
  • ...
  • XCont <0,0, (3)> ++ контейнер <(1 << (2))> ++ контейнер <(1 << (1))> ++ контейнер <(1 << (0) )>
  • XCont <0,0, (3)> ++ контейнер ++ контейнер ++ контейнер

Шаблоны C ++ ведут себя как сопоставление с образцом в Haskell. Поэтому мне проще думать об этом в простом стиле функций Haskell без каких-либо причудливых вещей в Haskell. Если кому-то интересно:

xcontainer :: Int -> String
xcontainer(e) = "struct XContainer:" ++ (
                   xcont( (e .>>. 1) , (e .&. 1) , (0) )
                ) ++ "{}"

xcont :: (Int,Int,Int) -> String
xcont(   0,0,shl) = "public XCont<0,0," ++ show(shl) ++ ">"
xcont(tail,0,shl) = (  xcont( (tail .>>. 1) , (tail .&. 1) , (shl+1) )
                    )
xcont(tail,1,shl) = (  xcont( (tail .>>. 1) , (tail .&. 1) , (shl+1) )
                    ) ++ "," ++ container(1 .<<. shl)

container :: Int -> String
container(e) = "public Container<" ++ show(e) ++ ">"

(Это допустимый язык Haskell, но в стиле написания не на языке Haskell.)

0 голосов
/ 23 октября 2010

Если вы делаете то, что я думаю, посмотрите на boost::variant, который делает именно то, что вы пытаетесь сделать.

...