Type map in C++ - c++

I'll begin with a context that will lead to the actual question.
I'm trying to build a class whose every instance will manage how data is tied together into objects.
The class should preferably contain methods:
class DataManager {
Object CreateObject();
void DestoryObject();
template<typename DataType>
DataType* AddDataToObject(Object o)
template<typename DataType>
DataType* GetDataForObject(Object o)
template<typename DataType>
void RemoveDataFromObject(Object o)
};
Object in the code above is just some identifier - int at this point and does not contain any data or methods (this should not change). DataType used above can be basically any class, however the general situation is that this is just a struct with no methods. The complete list of things that can be used as DataType is known at compile time but should not be encoded as it changes quite often.
The two goals I try to achieve are:
- Maintainability/Speed - The user should be able to add new DataType structures without modifying this code
- Speed - should be as fast as possible :)
Now the best thing idea I had so far is to make a container classes:
class ContainerBase;
template<typename DataType>
class DataTypeContainer : ContainerBase;
The data structure then would be something like:
map< DataTypeType, map< Object, ContainerBase* > >
Sow how can one achieve this?
Would boost::mpl::map help and how?
In essence this should be possible to do since all DataType's are known at compile time.

class DataManager {
struct internal_base { virtual ~internal_base() {} };
template<typename T> struct internal_data : public internal_base {
T t;
};
boost::unordered_map<Object, boost::unordered_map<std::string, boost::unique_ptr<internal_base>>> data;
public:
Object CreateObject() { return Object(); }
void DestroyObject(Object o) { data.erase(o); }
template<typename DataType> DataType* AddDataToObject(Object o, std::string name) {
internal_data<T>* ptr = new internal_data<T>();
data[o][name] = ptr;
return &ptr->t;
}
template<typename DataType> DataType* GetDataForObject(Object o, std::string name) {
internal_base* ptr = data[o][name].get();
if (internal_data<DataType>* dptr = dynamic_cast<internal_data<DataType>*>(ptr)) {
return &dptr->t;
else
return 0;
}
void RemoveDataFromObject(Object o, std::string name) {
data[o][name] = 0;
}
};
This code makes some assumptions- like default-construction of Object type, and that it is hashable. But it shouldn't be too difficult to modify. It would be substantially trickier to get defined behaviour if you want just one data member of each type associated with a specific Object, because you can't rely on RTTI to return unique names for each possible DataType.

If you want the equivalent of a map from types to values, and it can be global, you can use static members:
template <typename T>
struct DataManager {
static std::map<void*, Object> this_type_map;
};
plus appropriate definitions of DataManager<T>::this_type_map for the various values of T (but those definitions don't need to be in the same source file). After that, you can create type map objects using (void*)(new int), free them using delete (int*)(m), and look up the object for an instance m and a type T using DataManager<T>::this_type_map[m]. You would want to wrap these in functions or objects, of course. Note that you can have a different type than Object as the value type in the map, including (using template specializations) having a different value type for each key type in the type map.

I think that you need the std::tuple c++11 or playing with boost::tuple for c++ 03
template<typename T>
struct Entry{
T t;
};
int main(int argc, char **argv) {
std::tuple< int, float, double, Entry<int> > objects;
std::get<0>(objects) = 3;
std::get<3>(objects).t = 5;
//
utils::get< Entry<int> >(object).t = 5;
return 0;
}
get by type can be implemented like here:
https://github.com/alekstheod/tnnlib/blob/master/src/Utilities/Utilities/MPL/Tuple.h

You could create a tuple with the mapping between your types like that:
template < std::size_t sz, typename... Types >
struct TypeMap {
TypeMap (std::array< std::tuple< Types... >, sz > m) : mapping (m) {
}
std::array< std::tuple< Types... >, sz > mapping;
};
Then specify a function to convert
template < typename To, typename From, std::size_t sz, typename... T >
To convert (From from, TypeMap< sz, T... > m) {
for (auto entry : m.mapping) {
if (utils::get< From > (entry) == from) {
return utils::get< To > (entry); //Tricky part here
}
}
throw std::logic_error ("No entry in the typemap");
}
Then specify the mapping
const auto map = TypeMap{{std::make_tuple (red, "red", 1),
std::make_tuple (green, "green", 2),
std::make_tuple (blue, "blue", 3)}};
and finally you can call your convert function and convert any type to any other type ;)
See my article here:
https://cpptelepathy.wordpress.com/
You would need this file
https://github.com/alekstheod/tnnlib/blob/master/src/Utilities/MPL/Tuple.h
for util::get

Related

How to expand a non value paramter pack

I've been trying to expand a non value parameter pack recently in C++. Is this possible? And if it's not, why?
I mean, as you can see, in the line with the comment //, given a parameter pack for the TypeMap class, how can I call addType<T>() with each type of the parameter pack? Thanks in advance!
template <typename... T>
class TypeMap
{
using vari_cmps = std::variant<T*...>;
private:
template<typename Type>
void addType()
{
typemap[typeid(Type).name()] = std::make_unique<Type>(0).get();
}
public:
std::map<const char*, vari_cmps> typemap{};
TypeMap()
{
(addType<T,...>()); // Idk how to use this in order to make it work
}
~TypeMap()
{
typemap.clear();
}
};
As #HolyBlackCat has already answered in the comments, you can expand it like this:
TypeMap() {
(addType<T>(), ...);
}
If T is std::string, int, float this would expand to:
TypeMap() {
(addType<std::string>(), addType<int>(), addType<float>());
}
There are however a few more issues in this code-snippet:
1. addType()
addType() will not work as you'd expect, due to the unique_ptr deleteing your object after you put it into the map.
.get() only retrieves the pointer that the unique_ptr manages but does not transfer ownership, so the unique_ptr will still delete the pointed-to object once it gets out of scope, leaving a dangling pointer in your map.
so your addType() is roughly equivalent to:
template<typename Type>
void addType() {
Type* tptr = new Type(0); // unique pointer constructs object
typemap[typeid(Type).name()] = tptr; // insert pointer value of unique pointer
delete tptr; // unique pointer destructs
}
You could fix this by releasing the unique_ptr after inserting its value into the map & then cleaning it up in the destructor:
template<typename Type>
void addType() {
auto ptr = std::make_unique<Type>(0);
typemap[typeid(Type).name()] = ptr.get();
ptr.release(); // now unique_ptr won't delete the object
}
~TypeMap() {
// cleanup all pointers
for(auto& [name, ptrVariant] : typemap)
std::visit([](auto ptr) { delete ptr; }, ptrVariant);
}
2. Consider using std::type_index instead of const char* as map key
std::type_info::name() returns an implementation-defined name for the given type, so you have no guarantee that you will get an unique name for a given type.
Returns an implementation defined null-terminated character string containing the name of the type. No guarantees are given; in particular, the returned string can be identical for several types and change between invocations of the same program.
std::type_index on the other hand is build specifically for this purpose - using types as keys - and comes with all comparison operators & a std::hash specialization, so you can use it with std::map & std::unordered_map out of the box.
e.g.:
template <class... T>
class TypeMap
{
using vari_cmps = std::variant<T*...>;
private:
template<typename Type>
void addType()
{
typemap[std::type_index(typeid(Type))] = /* something */;
}
public:
std::map<std::type_index, vari_cmps> typemap{};
TypeMap() { /* ... */ }
~TypeMap() { /* ... */ }
template<class U>
U* get() {
auto it = typemap.find(std::type_index(typeid(U)));
return std::get<U*>(it->second);
}
};
Consider using std::tuple
std::tuple is basically built for this task, storing a list of arbitrary types:
e.g.:
template <class... T>
class TypeMap
{
private:
std::tuple<std::unique_ptr<T>...> values;
public:
TypeMap() : values(std::make_unique<T>(0)...) {
}
template<class U> requires (std::is_same_v<U, T> || ...)
U& get() { return *std::get<std::unique_ptr<U>>(values); }
template<class U> requires (std::is_same_v<U, T> || ...)
U const& get() const { return *std::get<std::unique_ptr<U>>(values); }
};
usage:
TypeMap<int, double, float> tm;
tm.get<int>() = 12;
If you want you can also store T's directly in the tuple, avoiding the additional allocations.

Eigen Allocator causing Trouble in Templated Class

I have this class:
typedef vector<Eigen::Affine3d,Eigen::aligned_allocator<Eigen::Affine3d> > VoteList;
template <class T>
class KernelDensityEstimator {
public:
KernelDensityEstimator() {}
void setData(vector<T>& d) {
data = d;
}
void setKernel(double (*k)(T&, T&)) {
kernel = k;
}
double evaluate(T& p) {
double sum;
for (int i = 0; i < data.size(); i++) {
sum += (*kernel)(p, data[i]);
}
return sum/data.size();
}
private:
double (*kernel) (T&, T&);
vector<T> data;
};
I want to use with with the type T = Eigen::Affine3d for now. However, when I call setData() it's causing me troubles because Eigen requires to specify an Eigen::aligend_allocator for std containers to use with Eigen.
So when I give a vector<Eigen::Affine3d,Eigen::aligned_allocator<Eigen::Affine3d> > (aka VoteList) as the input parameter to setData() my compiler complaines that:
no known conversion for argument 1 from ‘VoteList {aka std::vector<Eigen::Transform<double, 3, 2>, Eigen::aligned_allocator<Eigen::Transform<double, 3, 2> > >}’ to ‘std::vector<Eigen::Transform<double, 3, 2>, std::allocator<Eigen::Transform<double, 3, 2> > >&’
Which kind of makes sense, but I thought that the allocator is part of the object type. Is there a way around this and keeping my KernelDensityEstimator generic?
You can enable Eigen's std::vector specializations for the types you are planing to use, as detailed there, e.g.:
#include<Eigen/StdVector>
EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(Eigen::Affine3d)
EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(Eigen::Matrix4d)
...
You basically want to have a different type for your member data depending on the class template parameter type T. There are several ways to do that, here is one suggestion:
Define a special storage type for your class, in which you keep the data (best put it in a suitable namespace or inside your class):
//normal storage type
template<typename T>
struct storage_type_impl
{
using type = std::vector<T>;
};
//storage type for Eigen::Transform types
template<typename ... Args>
struct storage_type_impl<Eigen::Transform<Args ...> >
{
using type = std::vector<Eigen::Transform<Args ...>
, Eigen::aligned_allocator<Eigen::Transform<Args ...> > >;
};
template<typename T>
using storage_type = typename storage_type_impl<T>::type;
Now you can use that inside your class, together with a templated setData method (that is sufficient as the compiler will still complain if it cannot perform the data copy/move):
template <class T>
struct KernelDensityEstimator
{
template<typename D>
auto setData(D&& d)
{
data = std::forward<D>(d); //calls move assignment if d is an rvalue
//otherwise a copy is performed
}
//...
private:
storage_type<T> data;
//...
};
The code is untested as I don't have Eigen available at the moment, but I hope it's still able to explain the basic idea.

How to generalise this further using (variadic) templates

I am having trouble going the second step or level in templating my code. I have stripped the code to its bare essentials for readability.
I have looked through a lot of templates questions, but I was not really able to solve my exact issue.
I currently have a class RIVRecord, which I templated like this
template <class T>
class RIVRecord
{
private:
std::vector<T> values;
public:
std::string name;
RIVRecord(std::string _name, std::vector<T> _values) { name = _name; values = _values; };
~RIVRecord(void) { };
size_t size() {
return values.size();
}
T* Value(int index) {
return &values[index];
}
}
Easy enough. The T types are usually primitive types such as floats and integers. Then I want to put these RIVRecords in a DataSet class. Here is where I am having more difficulty. Untemplated it would be something like this:
class RIVDataSet
{
private :
//How to template this??
vector<RIVRecord<float>> float_records;
vector<RIVRecord<int>> int_records;
public:
RIVDataSet(void);
~RIVDataSet(void);
//And this
void AddRecord(RIVRecord<float> record) {
//How would this work?
}
//And this?
RIVRecord<float> GetFloatRecord();
};
I come from a Java background, so there I could use the vector<?> and do type checking whenever I ask a RIVRecord. But this does not seem possible in C++. I tried using variadic templates but am unsure how to construct the vector using all types in the template :
template <class... Ts>
class RIVDataSet
{
private :
//For each T in Ts
vector<RIVRecord<T>> records;
public:
RIVDataSet(void);
~RIVDataSet(void);
//For each T in Ts
void AddRecord(RIVRecord<T> record) {
//How would this work?
}
//For each T in Ts, get the record by index.
RIVRecord<T> GetRecord(int index);
};
I already saw that this sort of iteration in C++ templates is not possible, but it is just to clarify what I would want.
Any help is very welcome, thank you.
EDIT:
There is no restriction on the number of types (floats, ints,...) for T
Also, GetRecord works by having an index, but I don't really care about it that much, as long as I can iterate over the records and get the right type.
Solving this via variadic templates is not very complicated, but requires some additional support types. Let us begin, by looking at the result:
template <typename... V>
class many_vectors
{
static_assert(are_all_different<V...>::value, "All types must be different!");
std::tuple<std::vector<V>...> _data;
public:
template<typename T>
std::vector<T>& data()
{ return std::get<index_of<T, V...>::value>(_data); }
template<typename T>
std::vector<T> const& data() const
{ return std::get<index_of<T, V...>::value>(_data); }
template<typename T>
void push_back(T&& arg)
{ data<typename std::remove_reference<T>::type>().push_back(std::forward<T>(arg)); }
template<typename T, typename... W>
void emplace_back(W&&... args)
{ data<T>().emplace_back(std::forward<W>(args)...); }
};
The static_assert defines a very important requirement: Since we are differentiating on types, we must ensure that all types are different. The _data member is a std::tuple of the vectors for the different types, and corresponds directly to your float_records and int_records members.
As an example of providing a member function that refers to one of the vectors by their type the data function exposes the individual vectors. It uses a helper template to figure out which element of the tuple corresponds to your type and gets the result.
The push_back function of the vectors is also exposed to show how to use that to provide functions on these. Here std::forward is used to implement perfect forwarding on the argument to provide optimal performance. However, using rvalue references in combination with templates parameter deduction can lead to slightly unexpected results. Therefore, any reference on the T parameter is removed, so this push_back will not work for a many_vectors containing reference types. This could be fixed by instead providing two overloads push_back<T>(T&) and push_back<T>(T const&).
Finally, the emplace_back exposes a function that cannot rely on template parameter argument deduction to figure out which vector it is supposed to utilize. By keeping the T template parameter first, we allow a usage scenario in which only T is explicitly specified.
Using this, you should be ably to implement arbitrary additional members with similar funcitonality (e.g. begin<T> and end<T>).
Helpers
The most important helper is very simple:
template<typename T, typename U, typename... V>
struct index_of : std::integral_constant<size_t, 1 + index_of<T, V...>::value>
{ };
template<typename T, typename... V>
struct index_of<T, T, V...> : std::integral_constant<size_t, 0>
{ };
This will fail with a fairly ugly error message, if the first argument is not one of the following at all, so you may wish to improve on that.
The other helper is not much more complicated:
template<typename T, typename... V>
struct is_different_than_all : std::integral_constant<bool, true>
{ };
template<typename T, typename U, typename... V>
struct is_different_than_all<T, U, V...>
: std::integral_constant<bool, !std::is_same<T, U>::value && is_different_than_all<T, V...>::value>
{ };
template<typename... V>
struct are_all_different : std::integral_constant<bool, true>
{ };
template<typename T, typename... V>
struct are_all_different<T, V...>
: std::integral_constant<bool, is_different_than_all<T, V...>::value && are_all_different<V...>::value>
{ };
Usage
Yes, usage is as simple as you might hope:
v.push_back(int(3));
v.push_back<float>(4);
v.push_back<float>(5);
v.push_back(std::make_pair('a', 'b'));
v.emplace_back<std::pair<char, char>>('c', 'd');
std::cout << "ints:\n";
for(auto i : v.data<int>()) std::cout << i << "\n";
std::cout << "\n" "floats:\n";
for(auto i : v.data<float>()) std::cout << i << "\n";
std::cout << "\n" "char pairs:\n";
for(auto i : v.data<std::pair<char, char>>()) std::cout << i.first << i.second << "\n";
With the expected result:
ints:
3
floats:
4
5
char pairs:
ab
cd
You can use a technique called type erasure, you'll have to include another level of indirection however. Some general feedback:
RIVRecord(std::string _name, std::vector<T> _values)
Is better as:
RIVRecord(const std::string& _name, const std::vector<T>& _values)
In order to avoid unnecessary copies, overall the rule of thumb is to accept arguments as const& for most things which aren't a primitive.
T* Value(int index) { return &values[index]; }
Is dangerous, if the size() goes beyond capacity() of your vector< T > it will reallocate and invalidate all your pointers. A better interface in my opinion would be to have a T GetValue< T >() & void SetValue< T >( T a_Value ).
On to type erasure, this is how RIVDataSet could look, I'm using a library called Loki here, if you don't want to use Loki I'll give you some pointers afterwards.
class RIVDataSet
{
private :
//How to template this??
struct HolderBase
{
virtual ~HolderBase() {}
};
template< typename T >
struct HolderImpl : HolderBase
{
// Use pointer to guarantee validity of returned record
std::vector< RIVRecord< T >* > m_Record;
};
typedef Loki::AssocVector< Loki::TypeInfo, HolderBase* > HolderMap;
HolderMap m_Records;
public:
~RIVDataSet()
{
for( HolderMap::iterator itrCur = m_Records.begin(); itrCur != m_Records.end(); ++itrCur ) delete itrCur->second;
}
//And this
template< typename T >
void AddRecord(const RIVRecord< T >& record )
{
HolderMap::iterator itrFnd = m_Records.find( typeid( T ) );
if( itrFnd == m_Records.end() )
itrFnd = m_Records.insert( std::make_pair( Loki::TypeInfo( typeid( T ) ), new HolderImpl< T >() ) ).first;
static_cast< HolderImpl< T >* >( itrFnd->second )->m_Record.push_back( new RIVRecord< T >( record ) );
}
template< typename T >
RIVRecord< T >* GetRecord()
{
HolderMap::iterator itrFnd = m_Records.find( typeid( T ) );
assert( itrFnd != m_Records.end() );
return itrFnd == m_Records.end() ? 0 : static_cast< HolderImpl< T >* >( itrFnd->second )->m_Record.front();
}
};
Loki::AssocVector can be substituted for std::map, you do however need Loki::TypeInfo, which is just a wrapper for std::type_info. It's fairly easy to implement one your self if you take a look at the code for it in Loki.
One horrible idea if you really must do it as general is using the "type erasure idiom". It goes something like this (haven't compiled that though but I think it will, and can be further improved by type traits that would link RIVRecordsIndex::Float to the type float and prevent error)
class BaseRIVRecord
{
};
template <class T>
class RIVRecord : public BaseRIVRecord
{
};
enum class RIVRecordsIndex
{
Float, Int
};
class RIVDataSet
{
public:
template<RIVRecordsIndex I, typename T>
void addRecord()
{
allmightyRecords.resize(I+1);
allmightyRecords[I].push_back(new RIVRecord<T>());
}
template<RIVRecordsIndex I, typename T>
RIVRecord<T>* get(unsigned int index)
{
return static_cast<RIVRecord<T>*>(allmighyRecords[I][index]);
}
private:
std::vector<std::vector<BaseRIVRecord*>> allmightyRecords;
};
int main()
{
RIVDataSet set;
set.addRecord<RIVRecordsIndex::Float, float>();
set.addRecord<RIVRecordsIndex::Float, float>();
set.addRecord<RIVRecordsIndex::Int, int>();
RIVRecord<int> r = set.get<RIVRecordsIndex::Int, int>(0);
}
If you decide to do this stuff make sure you do not slice the inherited type (i.e. use vector of pointers). Do use some kind of type traits to prevent error calls like set.get. Again I have no time to actually compile that, it is just an idea thrown to further develop.
You can't use variadic templates to create multiple members of the same name but different type. In fact, you can never have two members with the same name. However, you can use multiple inheritance, and put the member in your base classes using variadic base classes. You can then use a member template in your derived class to resolve the ambiguity.
The example below also uses perfect forwarding to make sure that if a temporary is passed to add(), its resources can be "stolen". You can read more about that here.
Here is the example:
#include <vector>
#include <utility>
// This templated base class holds the records for each type.
template <typename T>
class Base {
public:
// "T &&v" is a universal reference for perfect forwarding.
void add(T &&v) { records.push_back(std::forward<T>(v)); }
private:
std::vector<T> records;
};
// This inherits from Base<int>, Base<double>, for example, if you instantiate
// DataSet<int, double>.
template <typename... Ts>
class DataSet : public Base<Ts>... {
public:
// The purpose of this member template is to resolve ambiguity by specifying
// which base class's add() function we want to call. "U &&u" is a
// universal reference for perfect forwarding.
template <typename U>
void add(U &&u) {
Base<U>::add(std::forward<U>(u));
}
};
int main() {
DataSet<int, double> ds;
ds.add(1);
ds.add(3.14);
}

C++ Looking up Types At Runtime

I have a class parameterised by some template parameters:
template<typename Scalar, typename Integrator, int Dimension>
class foo;
Each of the template parameters can be one of a few possible types. Currently the type of foo used is hard-coded in man typedef foo<...> foo_type. I wish to adapt my program so that a collection of foo's are supported; something like:
if (desired_foo_str == "2DSimpleFloat")
{
foo<float,Simple,2>(params).method();
}
else if (desired_foo_str == "3DSimpleDouble")
{
foo<double,Simple,3>(params).method();
}
else
{
std::cout << "Unsupported foo."
}
The interface of foo does not depend on its template parameters. My question is how can I improve this solution? I know boost::mpl provides a type vector but it seems more for compile time reductions as opposed to run-time switching.
Clarification
Lets say (this is a simplification) that my program takes a set of points in N-dimensions (provided by the user) and integrates them. Certain combinations of dimensions, integration methods and scalar types can be accelerated by SIMD (hence the use of template parameters). All combinations of foo<A,B,N> are valid however different users (all of whom will have compiled my program) will require only a couple of specific specializations for their work. I wish to allow for:
$ integrate --method=2DSimpleFloat mypoints2d.dat
$ integrate --methid=3DSimpleDouble mypoints3d.dat
so run-time selection of what method they wish to use. I am wondering what kind of frame-work best allows me to associate types with strings such that I can better handle the above scenario.
You could make templated default method which throws an error, and template-specializations per combination that you support.
class Simple {};
template<typename Scalar, typename Integrator, int Dimension>
class foo
{
public:
void method();
foo() {}
};
// default implementation throws an error
template<typename Scalar, typename Integrator, int Dimension>
void foo<Scalar,Integrator,Dimension>::method() { cout << "unsupported\n"; };
// override default for supported cases:-
template<>
void foo<double,Simple,2>::method() { cout <<"method1\n"; };
template<>
void foo<double,Simple,3>::method() { cout <<"method2\n"; };
// test program
void main() {
foo<float,Simple,2> a; a.method(); // output "unsupported"
foo<double,Simple,2> b; b.method(); // output "method1"
foo<double,Simple,3> c; c.method(); // output "method2"
}
You should be able to mix general purpose implementations and special purpose overides freely throughout the class; (e.g. perhaps some permeation can be handled with SIMD intrinsics or whatever)
If all the class methods were identical and generic, a convenient way to restrict use might be to restrict the constructor so that undesired cases can't be instantiated
in general if the mechanisms of overloading and templates are being used correctly, you should be able to avoid checking types manually where they're used.
This can all work compile time statically linked without any pointers or virtual dispatch.
If the supported implementations are to be the same, the over-rides can be wrappers to direct to another templated method as suggested above.
Your question doesn't provide enough information for a complete answer, but I have a hunch: Perhaps you should look into refactoring your code so as to separate the part that is independent of the parameters from the code that depends on the template parameters.
The typical example is taken from Scott Meyers's book. Suppose you have a square matrix multiplicator, and you write this as a full template:
template <typename T, unsigned int N>
Matrix<T, N> multiply(Matrix<T, N>, Matrix<T, N>)
{
// heavy code
}
With this setup, the compiler would generate a separate piece of code for each size value N! That's potentially a lot of code, and all that N provides is a bound in a loop.
So the suggestion here is to turn compile-time into runtime parameters and refactor the workload into a separate function, and only use template stubs to dispatch the call:
template <typename T>
void multiply_impl(unsigned int N,
MatrixBuf<T> const & in1, MatrixBuf<T> const & in1,
MatrixBuf<T> & out)
{
// heavy work
}
template <typename T, unsigned int N>
Matrix<T, N> multiply(Matrix<T, N> const & in1, Matrix<T, N> const & in1)
{
Matrix<T, N> out;
multiply_impl(N, in1.buf(), in2.buf(), out.buf());
}
You could do something similar: Put all the argument-independent code in a base class, and make the derived classes templates. The runtime can then use a factory function to create the correct concrete instance at runtime. As an alternative to inheritance you can also make a type-erasing wrapper class that contains a private pointer-to-base, and the runtime populates this with concrete derived implementation instances.
I'm guesing you are looking for register pattern. This is only my draft, so don't rely on it.
class AbstractFooFactory
{
virtual AbstractFoo* create( ParamsType cons& params ) = 0;
// or construct on stack and call .method()
virtual void createAndCallMethod( ParamsType cons& params ) = 0;
};
class FooRegister
{
~FooRegister(); // delete all pointers
template< typename FooFactory >
void operator() ( FooFactory const & factory ) // for boost::mpl:for_each
{ map[factory.getName()]= new FooFactory( factory ); }
AbstractFooFactory* get( std::string name );
std::map< std::string , AbstractFooFactory* > map;
};
template< typename Scalar, typename Integrator, typename Dimension >
class FooFactory: public AbstractFooFactory
{
typedef FooFactory<Scalar, Integrator, Dimension > type; // Metafunction
std::string getName(); // this will be a bit hard to implement
AbstractFoo* create( ParamsType cons& params );
void createAndCallMethod( ParamsType cons& params );
};
Simple trails may be used for storing type names:
template< typename Type >
struct NameTrails
{
static const char const* value;
};
template<> const char const* NameTrails<int>::value = "Int";
template<> const char const* NameTrails<float>::value = "Float";
template<> const char const* NameTrails<double>::value = "Double";
template<> const char const* NameTrails<Simple>::value = "Simple";
template<> const char const* NameTrails<Complex>::value = "Complex";
template< typename Scalar, typename Integrator, typename Dimension >
std::string FooFactory::getName()
{
return boost::lexical_cast<std::string>( Dimension::value ) + "D"
+ NameTrails< Integrator >::value
+ NameTrails< Scalar >::value;
}
And now you need to register all types using mpl::for_each:
FooRegister fooRegister;
typedef boost::mpl::vector<Simple,Complex> IntegratorsList;
typedef boost::mpl::vector<int,float,double> ScalarsList;
typedef boost::mpl::range_c<int,1,4> DimensionsList;
typedef boost::mpl::vector<
boost::mpl::vector< Simple, float, boost::mpl::int_<2> >,
boost::mpl::vector< Simple, double, boost::mpl::int_<3> >,
... other types or full cross join ... > FooList;
boost::mpl::for_each< FooList, boost::mpl::quote3<FooFactory> >(
boost::ref(fooRegister) );
What i don't know is how to cross join IntegratorsList, ScalarList, range_c<int,1,4> to constuct full FooList.
fooRegister.get("2DSimpleFloat")->createAndCallMethod(params);
You probably want to do this statically, so yes it is possible, but i find it rather difficult to achieve better performance then a simple dynamic map or hash map.

Template metaprogram converting type to unique number

I just started playing with metaprogramming and I am working on different tasks just to explore the domain. One of these was to generate a unique integer and map it to type, like below:
int myInt = TypeInt<AClass>::value;
Where value should be a compile time constant, which in turn may be used further in meta programs.
I want to know if this is at all possible, and in that case how. Because although I have learned much about exploring this subject I still have failed to come up with an answer.
(P.S. A yes/no answer is much more gratifying than a c++ solution that doesn't use metaprogramming, as this is the domain that I am exploring)
In principle, this is possible, although the solution probably isn't what you're looking for.
In short, you need to provide an explicit mapping from the types to the integer values, with one entry for each possible type:
template< typename T >
struct type2int
{
// enum { result = 0 }; // do this if you want a fallback value
};
template<> struct type2int<AClass> { enum { result = 1 }; };
template<> struct type2int<BClass> { enum { result = 2 }; };
template<> struct type2int<CClass> { enum { result = 3 }; };
const int i = type2int<T>::result;
If you don't supply the fallback implementation in the base template, this will fail for unknown types if T, otherwise it would return the fallback value.
Depending on your context, there might be other possibilities, too. For example, you could define those numbers within within the types themselves:
class AClass {
public:
enum { inta_val = 1 };
// ...
};
class BClass {
public:
enum { inta_val = 2 };
// ...
};
// ...
template< typename T >
struct type2int
{
enum { result = T::int_val }; // will fail for types without int_val
};
If you give more context, there might be other solutions, too.
Edit:
Actually there isn't any more context to it. I was looking into if it actually was possible, but without assigning the numbers itself.
I think Mike's idea of ordering is a good way to do this (again, for a fixed set of types) without having to explicitly assign numbers: they're implicitly given by the ordering. However, I think that this would be easier by using a type list. The index of any type in the list would be its number. I think something like the following might do:
// basic type list manipulation stuff
template< typename T1, typename T2, typename T3...>
struct type_list;
// meta function, List is assumed to be some instance of type_list
template< typename T, class List >
struct index_of {
enum { result = /* find index of T in List */ };
};
// the list of types you support
typedef type_list<AClass, BClass, CClass> the_type_list;
// your meta function
template< typename T >
struct type2int
{
enum { result = index_of<T, the_type_list>::result };
};
This does what you want. Values are assigned on need. It takes advantage of the way statics in functions are assigned.
inline size_t next_value()
{
static size_t id = 0;
size_t result = id;
++id;
return result;
}
/** Returns a small value which identifies the type.
Multiple calls with the same type return the same value. */
template <typename T>
size_t get_unique_int()
{
static size_t id = next_value();
return id;
}
It's not template metaprogramming on steroids but I count that as a good thing (believe me!)
Similiar to Michael Anderson's approach but this implementation is fully standards compliant and can be performed at compile time. Beginning with C++17 it looks like constexpr values will be allowed to be used as a template parameter for other template meta programming purposes. Also unique_id_type can be compared with ==, !=, >, <, etc. for sorting purposes.
// the type used to uniquely identify a list of template types
typedef void (*unique_id_type)();
// each instantiation of this template has its own static dummy function. The
// address of this function is used to uniquely identify the list of types
template <typename... Arguments>
struct IdGen {
static constexpr inline unique_id_type get_unique_id()
{
return &IdGen::dummy;
}
private:
static void dummy(){};
};
The closest I've come so far is being able to keep a list of types while tracking the distance back to the base (giving a unique value). Note the "position" here will be unique to your type if you track things correctly (see the main for the example)
template <class Prev, class This>
class TypeList
{
public:
enum
{
position = (Prev::position) + 1,
};
};
template <>
class TypeList<void, void>
{
public:
enum
{
position = 0,
};
};
#include <iostream>
int main()
{
typedef TypeList< void, void> base; // base
typedef TypeList< base, double> t2; // position is unique id for double
typedef TypeList< t2, char > t3; // position is unique id for char
std::cout << "T1 Posn: " << base::position << std::endl;
std::cout << "T2 Posn: " << t2::position << std::endl;
std::cout << "T3 Posn: " << t3::position << std::endl;
}
This works, but naturally I'd like to not have to specify a "prev" type somehow. Preferably figuring out a way to track this automatically. Maybe I'll play with it some more to see if it's possible. Definitely an interesting/fun puzzle.
I think it is possible to do it for a fixed set of types, but quite a bit of work. You'll need to define a specialisation for each type, but it should be possible to use compile-time asserts to check for uniqueness. I'll assume a STATIC_ASSERT(const_expr), like the one in Boost.StaticAssert, that causes a compilation failure if the expression is false.
Suppose we have a set of types that we want unique IDs for - just 3 for this example:
class TypeA;
class TypeB;
typedef int TypeC;
We'll want a way to compare types:
template <typename T, typename U> struct SameType
{
const bool value = false;
};
template <typename T> struct SameType<T,T>
{
const bool value = true;
};
Now, we define an ordering of all the types we want to enumerate:
template <typename T> struct Ordering {};
template <> struct Ordering<void>
{
typedef TypeC prev;
typedef TypeA next;
};
template <> struct Ordering<TypeA>
{
typedef void prev;
typedef TypeB next;
};
template <> struct Ordering<TypeB>
{
typedef TypeA prev;
typedef TypeC next;
};
template <> struct Ordering<TypeC>
{
typedef TypeB prev;
typedef void next;
};
Now we can define the unique ID:
template <typename T> struct TypeInt
{
STATIC_ASSERT(SameType<Ordering<T>::prev::next, T>::value);
static int value = TypeInt<T>::prev::value + 1;
};
template <> struct TypeInt<void>
{
static int value = 0;
};
NOTE: I haven't tried compiling any of this. It may need typename adding in a few places, and it may not work at all.
You can't hope to map all possible types to an integer field, because there are an unbounded number of them: pointer types with arbitrary levels of indirection, array types of arbitrary size and rank, function types with arbitrary numbers of arguments, and so on.
I'm not aware of a way to map a compile-time constant integer to a type, but I can give you the next best thing. This example demonstrates a way to generate a unique identifier for a type which - while it is not an integral constant expression - will generally be evaluated at compile time. It's also potentially useful if you need a mapping between a type and a unique non-type template argument.
struct Dummy
{
};
template<typename>
struct TypeDummy
{
static const Dummy value;
};
template<typename T>
const Dummy TypeDummy<T>::value = Dummy();
typedef const Dummy* TypeId;
template<typename T, TypeId p = &TypeDummy<T>::value>
struct TypePtr
{
static const TypeId value;
};
template<typename T, TypeId p>
const TypeId TypePtr<T, p>::value = p;
struct A{};
struct B{};
const TypeId typeA = TypePtr<A>::value;
const TypeId typeB = TypePtr<B>::value;
I developed this as a workaround for performance issues with ordering types using typeid(A) == typeid(B), which a certain compiler fails to evaluate at compile time. It's also useful to be able to store TypeId values for comparison at runtime: e.g. someType == TypePtr<A>::value
This may be doing some "bad things" and probably violates the standard in some subtle ways... but thought I'd share anyway .. maybe some one else can sanitise it into something 100% legal? But it seems to work on my compiler.
The logic is this .. construct a static member function for each type you're interested in and take its address. Then convert that address to an int. The bits that are a bit suspect are : 1) the function ptr to int conversion. and 2) I'm not sure the standard guarantees that the addresses of the static member functions will all correctly merge for uses in different compilation units.
typedef void(*fnptr)(void);
union converter
{
fnptr f;
int i;
};
template<typename T>
struct TypeInt
{
static void dummy() {}
static int value() { converter c; c.f = dummy; return c.i; }
};
int main()
{
std::cout<< TypeInt<int>::value() << std::endl;
std::cout<< TypeInt<unsigned int>::value() << std::endl;
std::cout<< TypeInt< TypeVoidP<int> >::value() << std::endl;
}
I don't think it's possible without assigning the numbers yourself or having a single file know about all the types. And even then you will run into trouble with template classes. Do you have to assign the number for each possible instantiation of the class?
type2int as compile time constant is impossible even in C++11. Maybe some rich guy should promise a reward for the anwser? Until then I'm using the following solution, which is basically equal to Matthew Herrmann's:
class type2intbase {
template <typename T>
friend struct type2int;
static const int next() {
static int id = 0; return id++;
}
};
template <typename T>
struct type2int {
static const int value() {
static const int id = type2intbase::next(); return id;
}
};
Note also
template <typename T>
struct type2ptr {
static const void* const value() {
return typeid(T).name();
}
};