building and accessing a list of types at compile time
Asked Answered
A

2

13

I am trying to achieve the following using c++ template metaprogramming. I wish to build up a list of types and then collect these types together and do further compile-time processing on the list. So for example:

foo.h:

class Foo { ... };
// INSERT ANY CODE HERE

bar.h:

class Bar { ... };
// INSERT ANY CODE HERE

main.h:

#include "foo.h"
#include "bar.h"

struct list_of_types {
  typedef /* INSERT ANY CODE HERE */ type;
};

I can insert any code into the slots above, so long as list_of_types::type resolves to some representation (e.g. a boost::mpl::vector) of a list containing the types Foo and Bar. The following restrictions apply:

  1. The code in foo.h should not know about the code in bar.h, and vice versa. It should be possible to change the order of #include directives in main.h and not change any other code.

  2. The code in main.h should not have to change if I include further headers that add further types to the list.

  3. The list of types must be available at compile time. I am planning to do further metaprogramming involving the list.

Around answered 9/9, 2013 at 15:29 Comment(4)
@Jarod42 How does std::tuple help here? Point 2 is a very sensible requirement – this makes the code open to extension without requiring change to existing code.Airman
Loki typelist? loki-lib.sourceforge.net/html/a00554.htmlArmelda
@AdamBurry: the main challenge is finding a way to build up lists in this way. There are many ways of representing lists of types (such as boost::mpl::vector or the possibilities you mentioned), but this is not the core issue.Around
It seems to me that you are going to need code in your headers to register the types with the type list.Armelda
M
23

A solution utilizing a common header, variadic templates and a macro:

// Header common.h

// A distinct Void type
struct Void {};

template <typename ...> struct concat;

template <template <typename ...> class List, typename T>
struct concat<List<Void>, T>
{
    typedef List<T> type;
};

template <template <typename ...> class List, typename ...Types, typename T>
struct concat<List<Types...>, T>
{
    typedef List<Types..., T> type;
};

template <typename...> struct TypeList {};

template <>
struct TypeList<Void> {};
typedef TypeList<Void> TypelistVoid;
#define TYPE_LIST TypelistVoid
// Header foo.h
#include <common.h>

class Foo { };

typedef typename concat<TYPE_LIST, Foo>::type TypeListFoo;
#undef TYPE_LIST
#define TYPE_LIST TypeListFoo
// Header bar.h
#include <common.h>

class Bar { };

typedef typename concat<TYPE_LIST, Bar>::type TypeListBar;
#undef TYPE_LIST
#define TYPE_LIST TypeListBar
// Header main.h 
#include "foo.h"
#include "bar.h"

struct list_of_types {
    typedef TYPE_LIST type;
};
// Or just typedef TYPE_LIST list_of_types;

// Test
#include <iostream>
#include <typeinfo>

template <template <typename ...> class List, typename T, typename ...Types>
void info();

template <typename T, typename ...Types>
inline void info(TypeList<T, Types...>) {
    std::cout << typeid(T).name() << std::endl;
    info(TypeList<Types...>());
}

template <typename T>
inline void info(TypeList<T>) {
    std::cout << typeid(T).name() << std::endl;
}

int main() {
    info(list_of_types::type());
    return 0;
}
Merkley answered 9/9, 2013 at 18:30 Comment(3)
Ahh yes this does it. Good idea to use a preprocesor symbol to store the changing global "state" of the list. As an aside, I was hoping to be able to wrap up the code that gets put into foo.h and bar.h with a macro like REGISTER_TYPE, but I guess with this scheme that isn't quite possible. You've definitely answered my question though - thank you!Around
I've found a different solution which only needs macros not includes, see https://mcmap.net/q/905430/-building-a-compile-time-list-incrementally-in-cCampanology
Does this need the Void type? Can't you just specialize for List<> instead of List<Void>?Tonita
B
-2
template <typename ... Types>
void info(TypeList<Types...>) {

  std::initializer_list<std::string> ls { typeid(Types).name() ... };

  for (auto& name : ls)
    std::cout << name << std::endl;
}

int main() {
  info(TYPE_LIST());
  return 0;
}
Blondie answered 24/1, 2019 at 9:30 Comment(3)
Please add an explanation about your answer so people will no why it is a good one.Rootlet
just a small improvement to the above solution, by using initializer_list, you don't have to write 3 info() to expand the template params.Blondie
how does this relate to the question ?Murguia

© 2022 - 2024 — McMap. All rights reserved.