I'm a C++ novice, and I'm essentially trying to figure out if I can use an STL container as a template which stores whatever type is being passed into it. I don't know if I need to use a class template or define a unique struct or what.
Here's essentially what I have, along with some commentary on what I'd like to achieve:
std::deque<template class T> messages; <--- ???
//the goal being not to store ANY type in this deque, but to somehow
//template it for each type that needs to be stored
//e.g. a different deque for an int, bool, ADT, etc.
template<class T> bool StoreMessage(T const &messageToStore){
messages<T>.push_back(messageToStore);
}
I have no idea how to even approach this or if this is even possible, but I really don't want to have to write functions for each type that needs to be stored, because there are a lot. Or use void*. I don't want to do that either due to safety and I would still have to explicitly define how to handle each type, even though the process is going to be exactly the same.
Thanks, guys!
Containers are already templated, so what you are trying to do is probably something like this:
template <typename T>
bool store_message(const T &message, std::deque<T> &container) {
container.push_back(message);
}
To call it, pass both a container by reference and the element:
std::deque<int> numbers;
int el = 5;
store_message(el, numbers);
Actually you can do something like:
#include <deque>
template <class T>
struct container {
static std::deque<T> messages;
};
template <class T>
std::deque<T> container<T>::messages;
template<class T> bool StoreMessage(T const &messageToStore){
container<T>::messages.push_back(messageToStore);
}
int main() {
int a = 10;
StoreMessage(a);
}
So you want to wrap your variable over additional templated structure and put it into static variable. You need to however declare the static variable to compiler allocate memory to this variable. This is done in lines: template <class T> std::deque<T> container<T>::messages;. To do it properly remember to move everything what is templated to a header file including the memory allocating/declaring stuff... This way you'll be able to access the messages among all your cpp/cc files. All you need to do is to include your created header file in it.
Related
It seems understanding template template parameters will kill me :(, Let me explain what misconception I made in my mind which confuses me:
template<class T>
class B {}; // A templated class
Here is other code:
template<template<class X> class Z = B> // The problem is in this line for me
class BB{};
Note the line in the parameter list of templated class BB, which is:
template<class X> class Z = B
Now, what stops C++ to think that Z is not another templated class Z?
I.e.,
template<class X> class Z {
}
rather than thinking class Z is a templated parameter itself.
Mankarse has answered your question, but I thought I'd chime in anyway.
Template template parameters are just like normal template type parameters, except that they match templates instead of concrete types:
// Simple template class
template <typename Type>
class Foo
{
Type m_member;
};
// Template template class
template <template <typename Type> class TemplateType>
class Bar
{
TemplateType<int> m_ints;
};
If it helps, you can kind of think of them as like function pointers. Normal functions just accept arguments like normal templates just accept types. However, some functions accept function pointers which accept arguments, just like template template types accept templates that accept types:
void foo(int x)
{
cout << x << endl;
}
void bar(void (*f)(int))
{
f(1);
f(2);
}
To answer your question in the comments: template template template parameters are not possible. However, the reason they are not possible is just because the standardisation committee decided that template templates were enough, probably to make lives easier for the compiler implementors. That being said, there's nothing stopping the committee from deciding that they are possible, then things like this would be valid C++:
template <template <template <typename> class> class TemplateTemplateType>
class Baz
{
TemplateTemplateType<Foo> m_foos;
};
typedef Baz<Bar> Example;
// Example would then have Bar<Foo> m_foos;
// which would have Foo<int> m_ints;
Again, you can see parallels in function pointers.
types <=> values
templates <=> functions of values
template templates <=> functions of functions of values
template template templates <=> functions of functions of functions of values
The analogous function to Baz would be:
void baz(void (*g)(void (*f)(int)))
{
g(foo);
}
Where would you use a template template template?
It's pretty far-fetched but I can think of one example: a really generic graph searching library.
Two common algorithms in graph searching are the depth-first search (DFS) and the breadth-first search (BFS). The implementation of the two algorithms is identical except in one regard: DFS uses a stack of nodes whereas BFS uses a queue. Ideally, we'd just write the algorithm once, with the stack/queue as an argument. Also, we'd want to specify the implementation container of the stack or queue, so that we could do something like:
search<Stack, Vector>( myGraph ); // DFS
search<Queue, Deque>( myGraph ); // BFS
But what is a Stack or a Queue? Well, just like in the STL a stack or a queue can be implemented with any kind of container: vectors, deques, lists etc. and could also be stacks of any element type, so our stacks or queues would have the interface:
Stack<Vector, int> // stack of ints, using a vector implementation
Queue<Deque, bool> // queue of bools, using a deque implementation
But Vector and Deque themselves are template types!
So finally, our Stack would be a template template like:
template <template <typename> class Storage, typename Element>
struct Stack
{
void push(const Element& e) { m_storage.push_back(e); }
void pop() { m_storage.pop_back(); }
Storage<Element> m_storage;
};
And our search algorithm would then have to be a template template template!
template <template <template <typename> class, typename> class DataStructure,
template <typename> class Storage,
typename Graph>
void search(const Graph& g)
{
DataStructure<Storage, typename Graph::Node> data;
// do algorithm
}
That would be pretty intense, but hopefully you get the idea.
Remember: template template templates are not legal C++, so this whole graph search thing won't actually compile. It's just a "what if?" :)
This is part of the syntax of the language (which is monstrous and massively context-dependent). If template<class X> class Z occurs in a template-parameter-list then it is interpreted as declaration of a formal parameter Z with the kind (like a meta-type; kinds classify types in the same way types classify values) "template class taking one class argument".
The usage examples in the accepted answer are misleading,
especially for beginners. Granted it's hard to come up with anything that won't be contrived, but we should at least contrive something that doesn't contradict the overall principles. Template parameters should be used only when the user of our interface can't specify the type of the template for one or the other reason, and we need to do it for them. In the Stack example we ask for both Storage and Element, only to instantiate Storage with that very Element, which is entirely unnecessary, the user can easily perform a basic substitution:
Stack<deque<int>> my_stack;
And all the stack needs to do is this:
template <typename Storage>
struct Stack
{
void push(typename Storage::const_reference e) { m_storage.push_back(e); }
void pop() { m_storage.pop_back(); }
Storage m_storage;
typename Storage::reference top() { return m_storage.back(); }
};
It doesn't in any way decide for the user what the element type is, so it does not need the template parameter. Hence the search becomes
template <template <typename> class DataStructure,
template <typename> class Storage,
typename Graph>
void search(const Graph& g, typename Graph::const_reference)
{
DataStructure<Storage<typename Graph::Node>> data;
// do algorithm
}
Here I guess we assume that internal Graph::Node type is not accessible to the user, and search is somehow a friend function of the Graph, which seems to make some sense. However, do we actually need to fill the structure with graph nodes, or simply references to them? Can the user not refer to the nodes in any way? If not, why is it called a graph, and not, say, slow_unordered_set? So lets imagine for a second they have an access to some node reference/pointer type, then they can do this:
search<Stack<vector<Graph::node_ptr>>>(graph, 10);
The function simplifies further to this:
template <typename StackStructure, typename Graph>
void search(const Graph& g, typename Graph::const_reference)
{
StackStructure data;
// do algorithm
}
Gosh darn it, now it's more generic than ever! Do you want to specify an allocator for the storage? No problem, just do it. You instead wanted some statically allocated vector that requires maximum size parameter? Go right ahead. Want to implement the stack from scratch altogether? Well, as long as it quacks like a stack...
Perhaps a more appropriate example
of a template with template parameters would be some class that represents a complex system and uses some Storage template for a bunch of internal structures, and for some reason is parameterized on that Storage template:
template <template <typename> class Storage>
class System
{
Storage<Component_1> components_1;
Storage<Component_2> components_2;
Storage<Component_3> components_3;
Storage<MetaInfo> registry;
public:
// some inane interface
};
If you ask me - this code reeks, but it's not like I wouldn't write it.
Now that we have this semi-appropriate example for a template with a template parameter, we can contrive something for a template with a template parameter that itself has a template parameter: Imagine somehow we end up with like 10 of these System classes that all have the same interface, all parameterized on a Storage template, but otherwise very VERY different. Brace yourselves for the SuperSystem, an even more complicated class, that uses a bunch of our systems, but CRUCIALLY needs to decide itself what Storage templates to use with each system.
template< template< template <typename> class Storage> class System>
class SuperSystem
{
System<Vector> system_1;
System<OtherVector> system_2;
System<List> system_3;
public:
// absolutely bonkers interface
};
We want to specify something down the template hierarchy we're dealing with here, but still leave something up the hierarchy customizable. For some reason we don't know what exact system we will be dealing with, but we know something very specific about all of them, that we absolutely need to go our way. This is an overarching theme with these examples, our goal is not to make things more generic and customizable, but the opposite - we want to lock down certain deeply embedded things.
TL;DR
In my experience you would only encounter good use cases for templates with template parameters when knee deep in a meta programming library. A rule of thumb: if you can recognize this pattern
template<...> struct f { typedef ... type; };
as a type function, then in that mindset you are allowed to use templates with template parameters, and maybe ponder about anything deeper. Otherwise slap yourself on the wrist.
Is it possible to make an array of a struct with template type fields?
template<typename T>
struct MyStruct {
T *pField;
};
MyStruct< ?? > mystruct_arr[] = {
{ pFieldOfType1 },
{ pFieldOfType2 },
};
The above obviously doesn't work, but is it possible with other techniques?
I am trying to loop through the array mystruct_arr and call this function on every struct row:
template<typename T>
void SetupField(T &pSourceField, ...)
{
Base *field = ...->findBaseFieldFromDatabase(...);
...
pSourceField = static_cast<T>(field);
...
}
The reason is to try to refactor a very repetitive piece of code where I have to static_cast a long list of different types according to a few different arguments, without making it overly complicated.
A template is not a class, or a struct. It can be thought of as a blueprint, or a recipe, for a class or a struct that gets created when the template gets instantiated.
A template becomes an actual class or a struct only when it is instantiated, by specifying the required template parameters:
MyStruct<int>
Now you have an actual, living, breathing class here. But a MyStruct<int> would be a completely different class than MyStruct<char>. Without specifying the template parameters, MyStruct is not a class, or a struct, or anything that takes up a single byte of RAM. It's just a template for some struct or class.
But with template parameters, such as MySutrct<int>, this becomes an actual class with fields, and perhaps methods. Now that you have a class you can certainly have an array of these, now:
MyStruct<int> mystruct_arr[] = {
};
Or you could have a different array of MyStruct<char>s:
MyStruct<char> mystruct_arr2[] = {
};
But you can't have an array that contains both of these for the same, exact, precise reason that you can't have a single array containing a smorgasbord of different types and classes. You can't have an array that contains both chars, ints, floats, pointers, or various classes. An array always contains values of the same type/class. So pick one particular MyStruct<whatever>, and make an array out of it, that's all you can do.
But you can also declare another structure, now:
struct many_structs {
MyStruct<int> int_struct;
MyStruct<char> char_struct;
// ...
};
This sort of starts to look like an array you would like to have. But it's not an array. It's just an ordinary struct; and instead of using an array index to access a particular template instance, you would refer to the struct member directly.
You could, with some additional work, specialize std::get for your structure, and make this structure look like an array. But now you just realized that you reinvented std::tuple, and can simply to do this:
std::tuple<MyStruct<int>, MyStruct<char>> mystruct_tuple;
Finally, the closest approximation to what you're trying to do is possible only with some additional work, and with a C++ compiler that supports C++17, by declaring an array containing std::anys, or perhaps std::variants. If the array should only contain an a limited enumeration of template instances, std::variant gives the most type-safety, and convenience:
std::variant<MyStruct<int>, MyStruct<char>> mystruct_arr[]={
};
The resulting array contains only these two particular template instances. And with std::any, the screws get loosened further but you'll have to do more work to use and access each value in the array.
You think you want an array of templates.
What you really want is an array of types you can call a particular template function on.
The first is not possible. The second is called type erasure in C++.
template<class T>
using setup_fptr=void(*)(T &, Foo)
using gen_setup_ptr=void(*)(void*, Foo);
template<class T>
setup_ptr<T> get_setup(){ return SetupField<T>; }
template<class T>
gen_setup_ptr get_gen_setup(){
return [](void* p, Foo f){ get_setup<T>( *static_cast<T*>(p), f ); };
}
struct can_setup {
void* ptr=0;
gen_setup_ptr f=0;
can_setup(can_setup const&)=default;
can_setup& operator=(can_setup const&)=default;
can_setup()=default;
explicit operator bool() const{return f;}
template<class T>
can_setup(T* pt):
ptr(pt),
f( get_gen_setup<T>() )
{}
void setup( Foo foo ) const {
f(ptr, foo );
}
};
store an array of can_setup. Loop over them calling .setup(foo).
Foo is a placeholder for whatever other args you are using.
This technique is known as type erasure; we forget (erase) everything about T except that we can setup it.
I have a template function that I want to store a pointer to inside a std::vector.
The function looks like this:
template<typename T> void funcName(T& aT, std::vector<std::string>& fileName){...}
Now I want to store multiple pointers to functions of this kind inside a std::vector. For non-template functions I would do it like this:
typedef std::vector<std::string> string_vt;
typedef void func_t(T&, string_vt&);
typedef func_t* funcPointer;
typedef std::vector<funcPointer> funcPointer_vt;
But what is the correct syntax for template functions? How can I store them?
EDIT: First of all, thank you for your fast response. This was my first Question on Stack Overflow, so I am sorry for not providing enough information.
The set of T is finite, it can either be of type ClassA or type classB. In these function templates I want to do changes to T (so either ClassA or ClassB) with some hard coded data. I have 8 of these functions, which basically initiate a default constructed T with data specific to the function. In my program, I want to initiate 2*8 default constructed T's (8 ClassA and 8 ClassB). Therefore I run a for loop, calling one function after the other, to initiate my T objects with the function's body data.
for(int i = 0; i < initT.size(); ++i){
init_T[i]<T>(someT, fileName);
}
The for loop has as much iterations as there are function pointers inside the vector. At every iteration the function is called with some previously default constructed T and some other parameter. At the end the goal is to have 8 initiated T's with data specific to the function.
EDIT2: In case it helps, here is some actual source code. Inside the following function template I want to access my vector of function pointers in order to call the respective function.
template<typename T_Relation, typename T_Relation_Vec, bool row>
void bulk_load(initRelation_vt& aInitFunctions, T_Relation_Vec& aRel_Vec, const bool aMeasure, const uint aRuns, const char* aPath)
{
for(size_t i = 0; i < aRuns; ++i)
{
MemoryManager::freeAll();
aRel_Vec.clear();
string_vt fileNames;
for(size_t j = 0; j < aInitFunctions.size(); ++j)
{
aRel_Vec.emplace_back(T_Relation());
aInitFunctions[j]<T_Relation>(aRel_Vec[j], fileNames);
BulkLoader bl(fileNames[j].c_str(), tuples, aRel_Vec[j], delimiter, seperator);
Measure lMeasure;
if(aMeasure)
{
lMeasure.start();
}
try
{
bl.bulk_load();
if(row)
{
BulkInsertSP bi;
bi.bulk_insert(bl, aRel_Vec[j]);
}
else
{
BulkInsertPAX bi;
bi.bulk_insert(bl, aRel_Vec[j]);
}
}
catch(std::exception& ex)
{
std::cerr << "ERROR: " << ex.what() << std::endl;
}
lMeasure.stop();
if(aMeasure)
{
std::ofstream file;
file.open (aPath, std::ios::out | std::ios::app);
//print_result(file, flag, lMeasure.mTotalTime());
file.close();
}
}
}
}
This line is where the vector of function template pointers is accessed.
aInitFunctions[j]<T_Relation>(aRel_Vec[j], fileNames);
Templates are an advanced technique for static polymorphism. In a typed language, like C++, without static polymorphism you would have to separately define every entity used and precisely indicate every entity referred to.
Mechanisms of static polymorphism in C++ allow to automate indication of function or method and defer it until build via overloading. It allows you to define multiple entities sharing some characteristics at once via templates and defer definition of particular specializations until build, inferred from use.
(Notice that in various scenarios, static polymorphism allows separate code, so that changes to use and to definition are independent, which is very useful.)
The important implication of this mechanism is that every specialization of your template may be of different type. It is unclear, as of when I'm responding, whether you want to store pointers to a single or multiple types of specialization in one type of container. The possibilities depend also on parameter and result types of the function template.
A function in C++ has a type that is a combination of list of its parameter types and its return type. In other words, two functions that take and return the same types are of the same type. If your function template neither took or returned template parameter type (ie. T) nor templated type (eg. std::vector<T>), every specialization of this function template would be taking and returning the same types and would therefore be a function of the same type.
template <typename T>
int func() { ... }
This (arguably useless) function template takes no arguments and returns int, whatever T is used to specialize the template. Therefore a pointer to it could be used wherever the parameter is defined as int (*f)(). In this case you could keep pointer to any specialization in one vector.
typedef std::vector<std::string> string_vt;
typedef int func_t();
typedef func_t* funcPointer;
typedef std::vector<funcPointer> funcPointer_vt;
funcPointer x = &func<int>;
funcPointer y = &func<float>;
As can be seen, every specialization of your function template is of the same type and both pointers fit in the same container.
Next case - what if function header depends on a template parameter? Every specialization would have a different signature, that is a different function type. The pointers to all of them would be of different types - so it wouldn't be possible to even typedef this pointer once.
template <typename T>
void func(std::vector<T> param) { ... }
In this case function template specialization is of different type depending on T used to specialize.
typedef int func_t_int(std::vector<int>);
typedef func_t_int* funcPointerInt;
typedef std::vector<funcPointerInt> funcPointerInt_vt;
typedef float func_t_float(std::vector<float>);
typedef func_t_float* funcPointerFloat;
typedef std::vector<funcPointerFloat> funcPointerFloat_vt;
funcPointerInt x = &func<int>;
funcPointerFloat x = &func<float>;
Specializations are of different types, because they take different type of vectors. Pointers do not fit in the same container.
It's mention-worthy at this point, that in this case it's not necessary to define every pointer type separately. They could be a template type:
template <typename T>
using funcPointer = void (*)(std::vector<T>);
Which now allows funcPointer<int> to be used as a type qualifier, in place of earlier funcPointerInt.
funcPointer<float> y = &func<float>;
In more complicated situations a template could be created, whose every specialization is of a different type, and then would use a single instance of concrete vector to store various pointers to functions of type of only one of the specializations of your template. Although a simple template like in the example can only produce a single function per type, because every specialization yields one type of function and one function of that type, it's not impossible to conceive a scenario where various pointers to functions are obtained, both to specializations and usual functions, perhaps from various sources. So the technique could be useful.
But yet another scenario is that despite every specialization of the template being of different type, there's a need to store pointers to various specializations in single std::vector. In this case dynamic polymorphism will be helpful. To store values of different types, fe. pointers to functions of different types, in one type of variable, requires inheritance. It is possible to store any subclass in a field defined as superclass. Note however, that this is unlikely to accomplish anything really and probably not what you're really looking for.
I see two general possibilities now. Either use a class template with a method, which inherits from a non-template class.
template <typename T>
class MyClass : BaseClass
{
public:
T operator()(const T& param, int value);
}
MyClass<int> a;
MyClass<float> b;
BaseClass* ptr = &a;
ptr = &b;
While every specialization of this class may be of a different type, they all share superclass BaseClass, so a pointer to a BaseClass can actually point to any of them, and a std::vector<funcPointerBase> can be used to store them. By overloading operator() we have create an object that mimics a function. The interesting property of such a class is that it can have multiple instances created with parameter constructors. So effectively class template produces specializations of multiple types, and in turn every specialized class can produce instances of varying parametrization.
template <typename T>
class MyClass : BaseClass
{
int functor_param;
public:
MyClass(int functor_param);
T operator()(const T& param, int value);
}
This version allows creation of instances that work differently:
MyClass<int> a(1);
MyClass<int> b(2);
MyClass<float> c(4);
MyClass<int>* ptr = &a;
ptr = &b;
ptr = &c;
I am no expert on functors, just wanted to present the general idea. If it seems interesting, I suggest researching it now.
But technically we're not storing function pointers, just regular object pointers. Well, as stated before, we need inheritance to use one type of variable to store values of various types. So if we're not using inheritance to exchange our procedural functions for something dynamically polymorphic, we must do the same to pointers.
template <typename T>
T func(std::pair < T, char>) {}
template <typename T>
using funcPointer = T(*)(std::pair<T, char>);
template <typename T>
class MyPointer : BasePointer
{
funcPointer<T> ptr;
public:
MyPointer(funcPointer<T> ptr);
T()(std::pair <T, char>) operator*(std::pair <T, char> pair)
{
*ptr(pair);
}
};
This, again, allows creation of single std::vector<BasePointer> to store all possible pseudo-function-pointers.
Now the very important bit. How would You go about calling those, in either scenario? Since in both cases they are stored in a single std::vector<>, they are treated as if they were of the base type. A specific function call needs parameters of specific type and returns a specific type. If there was anything that all subclasses can do in the same way, it could be exposed by defining such a method in base class (in either scenario using functors or pointer..ors?), but a specific specialized function call is not that kind of thing. Every function call that You would want to perform in the end, after all this struggle, would be of a different type, requiring different type of parameters and/or returning different type of value. So they could never all fit into the same place in usual, not templated code, the same circumstances in execution. If they did, then dynamic polymorphism wouldn't be necessary to solve this problem in the first place.
One thing that could be done - which is greatly discouraged and probably defeats the purpose of dynamic polymorphism - is to detect subclass type at runtime and proceed accordingly. Research that, if you're convinced you have a good case for using this. Most likely though, it's probably a big anti-pattern.
But technically, anything you may want to do is possible somehow.
If I have correctly understood you, I may have a really simple and efficient solution:
template<class...Ts>
struct functor{
//something like a dynamic vtable
std::tuple<void(*)(Ts&,std::vector<std::string>&)...> instantiated_func_ptr;
template<class T>
void operator ()(T& aT,std::vector<std::string>& fileName){
get<void(*)(T&,std::vector<std::string>&)>(instantiated_func_ptr)
(aT,fileName);
}
};
VoilĂ !!
Until c++17, get<typename> is not defined so we have to define it (before the definition of the template functor above):
template<class T,class...Ts>
struct find_type{
//always fail if instantiated
static_assert(sizeof...(Ts)==0,"type not found");
};
template<class T,class U,class...Ts>
struct find_type<T,U,Ts...>:std::integral_constant<size_t,
find_type<T,Ts...>::value+1>{};
template<class T,class...Ts>
struct find_type<T,T,Ts...>:std::integral_constant<size_t,0>{};
template<class T,class...Ts>
constexpr decltype(auto) get(const std::tuple<Ts...>& t){
return get<find_type<T,Ts...>::value>(t);
}
And an example to show how to use it:
struct A{
void show() const{
std::cout << "A" << "\n";
}
};
struct B{
void show() const{
std::cout << "B" << "\n";
}
};
template<class T>
void func1(T& aT,std::vector<std::string>& fileName){
std::cout << "func1: ";
aT.show();
}
template<class T>
void func2(T& aT,std::vector<std::string>& fileName){
std::cout << "func2: ";
aT.show();
}
template<class T>
void func3(T& aT,std::vector<std::string>& fileName){
std::cout << "func3: ";
aT.show();
}
using functorAB = functor<A,B>;
int main(){
auto functor1=functorAB{{func1,func1}};//equivalent to functorAB{{func1<A>,func1<B>}}
auto functor2=functorAB{{func2,func2}};
auto functor3=functorAB{{func3,func3}};
auto v=std::vector<functorAB>{functor1,functor2,functor3};
auto a=A{};
auto b=B{};
auto fileNames = std::vector<std::string>{"file1","file2"};
for(auto& tf:v)
tf(a,fileNames);
for(auto& tf:v)
tf(b,fileNames);
}
In practice it is just a reproduction of the virtual call mechanism,
the tuple in functor is kind of virtual table. This code is not
more efficient than if you had written an abstract functor with virtual
operator() for each of your class A and B and then implemented it for each of
your functions... but it is much more concise, easier to maintain and may produce less binary code.
I'm trying to define a template that can handle user defined types and structures. The issue so far is that I can not get information of the structure members through the type definition. The template is suppose to detect structure data type and handle different member types accordingly.
The solution I came up so far involves a vector of type strings that signal the member element count and type:
std::vector<std::string> typeList
The program then traverse the list and handle structure according to typeList. There is so far no checking against given structure at all so behavior is undefined if a different structure is passed in.
My question is that is there any elegant way that can achieve this? I'd prefer the solution to check the incoming structure itself to get such type list information instead relying on a second data structure.
Thanks ahead.
I think what your looking for is template specialization.
E.g we could write a serialize method which is implemented differently for specific types:
template<typename T>
void serialize(const T& val)
{
static_assert("No implementation for this type provided");
}
template<>
void serialize<std::string>(const std::string& val)
{
// std::string specific implementation here
}
Notice that partial specialization (specialization for mutli types) is only possible for class templates.
There is no way to inspect types genericaly (like in java or c#) but you can have your types providing informations on their own.
template<typename T>
class GenericSerializeable {};
struct Foo : public GenericSerializeable<Foo>
{
int bar;
static std::vector<std::string> GetMembers()
{
return {"bar"}; // we could of course return a more complex type here providing more informations
}
};
// lets assume for a moment here that partial specialization is possible for functions, this is just a proof of concept
template<typename T>
void serialize(const GenericSerializeable<T>& type)
{
for (auto name : T::GetMembers())
{
// do whatever you want with them here
}
}
The combination with template specialization and a "tag class" allows is to treat types aswell not providing this interface with the same function signature.
I'm using template functions for object construction to create objects from reflection data, and it works pretty well, but now I want to support STL container types in the reflection system so that objects such as:
// Note - test case only
// for real world usage it would probably not be structured like this
// and the phrases would be mapped by an id or something
struct Phrases {
std::vector<std::string> phrases;
};
typedef std::string Lang;
struct Langs {
std::map< Lang, Phrases > translations;
};
Can be supported. I can do some regex magic on the return of
typeid( object ).name()
to figure out if an object is a vector or a map, and what the parameter arguments for the object is. And I have tried some template magic to do it something like the following, where CreateString, ConstructString & DestroyString are stand in functions and the data is stand in as well for something a bit more complex that uses a type database to handle object construction.
// Representational of code, basically a copy-paste to a different test project where I can work out the problems with this specific vector problem
// Vector specialised construction
template <typename T> void ConstructVector( void* object, const std::vector<std::string>& data ) {
T* vec = (T*)object;
Name vector_type = GetVectorTypeName<T>();
void *obj;
CreateString(&obj);
// All fields in this type should be valid objects for this vector
for( std::vector<std::string>::const_iterator it = data.begin(), end = data.end(); it != end; ++it ) {
// Push it
vec->push_back(*obj);
// Get address to new instance
void *newly = &vec->back();
ConstructString(newly,*it);
}
DestroyString(&obj);
}
Which doesn't work owing to the illegal indirection with "vec->push_back(*obj);" which I can't case because I don't actually know the type. Basically what I need to be able to do is create this vector with some blank unset elements already in it, or add new elements to it without actually having the type, because if I can get a pointer to a memory block inside the vector I can roll with that and construct the object. But the vector add requirements such as
vector::push_back( T& value )
or
vector::insert( Iter&, T& )
Won't work for me unless I can get my hands on that T type from inside the template
pastebin of testing code to try and solve this:
http://pastebin.com/1ZAw1VXg
So my question is, how can I get the std::string part of a std::vector declaration when I'm inside a template like
template <typename T> void SomeFunc() {
// Need to get std::string here somehow
// Alternatively need to make the vector a certain size and then
// get pointers to it's members so I can construct them
}
SomeFunc<std::vector<std::string>>>();
There are two ways to accomplish this.
1) Either you make use of the fact that std::vector<> (like all standard library container classes) maintains a member type value_type, which represents the type of the elements stored in the vector. So you can do this:
template <typename T> void SomeFunc() {
typename T::value_type s; // <--- declares a `std::string` object
// if `T` is a `std::vector<std::string>`
}
2) Or else, you change the declaration of your function and make use of template template parameters:
template <template <typename> class T, typename Elem>
void SomeFunc(T<Elem> &arg)
{
Elem s;
}
However, there is a small problem with that: std::vector is really a template with two parameters (element type and allocator type), which makes it a little difficult to use the template template parameters and still keep the syntax simple. One thing that worked for me is to declare an alias of the vector type that leaves only one template parameter:
template <typename Elem>
using myvector = std::vector<Elem>;
Then I can use SomeFunc like this:
int main()
{
myvec<std::string> vec;
SomeFunc(vec);
}
In c++11, you can use decltype and std::decay to that effect:
std::vector<int> vec;
using T = typename std::decay<decltype(*vec.begin())>::type;