I have a class called DBDriver that handles communication with a given table in a database. Its public entry point is a function template called execute_query(), which executes SELECT queries. Upon calling this function, some database logic is performed, and then a provided container (of the template type) is populated with results. This looks something like the following:
class DBDriver {
...
template <typename CONT_T>
void execute_query(const std::string& query, CONT_T& container);
...
};
template <typename CONT_T>
void DBDriver::execute_query(const std::string& query, CONT_T& container) {
DBCursor& cursor = ... // some database logic here
populate_container(container, cursor);
}
Of course, the above will not compile, as populate_container() is not defined in DBDriver.
DBDriver should be purely virtual and have several classes derive from it (one for each database table involved). Each derived class will define its own overloads of populate_container(), one for each relevant container type. This will look something like the following:
class SampleTableDBDriver : public DBDriver {
// ...
populate_container(const ContainerTypeOne& container, DBCursor& cursor);
populate_container(const ContainerTypeTwo& container, DBCursor& cursor);
// ...
};
My original attempt at this was unsuccessful, as I would have needed to define a virtual function template in DBDriver to serve as an entry point to a derived class' populate_container() overloads. (Of course such a thing does not exist in C++, hence my issue.)
Is there a cleaner, idiomatic solution for this type of problem?
The reason why execute_query is a template function is you need a generic container. What if you define a Interface for the container?
class IContainer
{};
Template Functions cannot be virtual. Therefore, you can use the Template Method Design Pattern.
class DBDriver
{
public:
void execute_query(const std::string& query, IContainer **_ppContainer);
{
DBCursor& cursor = ... // some database logic here
populate_container(_ppContainer, cursor);
}
virtual void populate_container(IContainer **_ppContainer, DBCursor &_dbCursor) = 0;
};
And let every derived class implement populate_container and also provide their custom Container.
class SampleTableDBDriver : public DBDriver
{
public:
class ContainerTypeOne : public IContainer
{};
void populate_container(IContainer **_ppContainer, DBCursor &_dbCursor)
{
ContainerTypeOne *pContainer = new ContainerTypeOne();
//....
(*_ppContainer) = pContainer;
}
};
SampleTableDBDriver oSampleDriver;
IContainer *pContainer = NULL;
std::string szQuery = // some query ;
oSampleDriver.execute_query(szQuery, &pContainer);
if(pContainer != NULL)
{
SampleTableDBDriver::ContainerTypeOne *pSampleDriverContainer =
dynamic_cast<SampleTableDBDriver::ContainerTypeOne*>(pContainer);
//use pSampleDriverContainer
}
Edit: For supporting multiple containers.
In your original design the populate_container seems to be overloaded in derived classes. In that case you would still pass the exact container from outside while calling execute_query.
Same thing can be done with this Template Method design. Then, you will need to decipher the type of container inside the populate_container function as follows:
New signature : int populate_container(IContainer *_pContainer, DBCursor &_dbCursor)
int populate_container(IContainer *_pContainer, DBCursor &_dbCursor)
{
if(dynamic_cast<ContainerTypeOne *>(_pContainer) != NULL)
{
ContainerTypeOne *pContainerOne = _pContainer;
//populate the result by using pContainerOne
return 1;
}
if(dynamic_cast<ContainerTypeTwo *>(_pContainer) != NULL)
{
ContainerTypeOne *pContainerTwo = _pContainer;
//populate the result by using pContainerTwo
return 1;
}
//no, I do not support the container you passed.
return 0;
}
SampleTableDBDriver oSampleDriver;
SampleTableDBDriver::ContainerTypeOne oSampleContainerTypeOne;
std::string szQuery = // some query ;
if(oSampleDriver.execute_query(szQuery, &oSampleContainerTypeOne) != 0)
{
//use oSampleContainerTypeOne;
}
You have options to choose from:
If you don't need populate_container() to be a member of DBDriver and can extend (or define) container interface, just implement
ContainerTypeOne::populate(DBCursor& cursor);
Make populate_container() a friend (in case you need access to private members of DBDriver):
template <typename CONT_T> friend void execute_query(const std::string& query, CONT_T& container);
Make populate_container() a non-member template function (if you don't need access to private members of DBDriver)
One way to do this is to employ the separation of concerns principle.
Querying databases goes to its own class hierarchy, and populating container goes to its own separate class hierarchy. These two hierarchies know nothing about each other, e.g. DBDriverThirteen knows nothing about ContainerFortyTwo. The two hierarchies only touch at their roots, i.e. DBDriver (and so its every derived class) knows about IContainerPopulator but nothing about any specific containers.
You may have a template that builds a specific ContainerPopulator for each container type. Assume for simplicity that you only need to support standard containers of strings that implement push_back.
struct IContainerPopulator {
virtual void push_back(const std::string&) = 0;
};
template <class CONT_T>
struct ContainerPopulator : IContainerPopulator {
StandardContainerPopulator (CONT_T& cont) : cont(cont) {}
void push_back(const std::string& s) override { cont.push_back(s); }
private:
CONT_T& cont;
};
Now you can do this
template <typename CONT_T>
void execute_query(const std::string& query, CONT_T& container) {
execute_query_adapted(query, ContainerPopulator<CONT_T>(container));
}
// no template!
virtual void execute_query_adapted(const std::string&,
IContainerPopulator&&) = 0;
By now you have probably recognised that IContainerPopulator is nothing but a specialised poor man's function binder. Why write another one if we have pretty good support in the language and its standard library? If you only need to support push_back, you can alternatively do this:
template <typename CONT_T>
void execute_query(const std::string& query, CONT_T& container) {
execute_query_adapted2(query,
[&](const std::string& s){container.push_back(s);});
}
// no template
virtual void execute_query_adapted2(const std::string&,
std::function<void(const std::string&)>) = 0;
Now if you need more than just push_back (or any fixed set of functions) and/or more than std::string (or any fixed set of types), things can get really hairy. More information would be needed about potential implementations of populate_query.
As long as you don't mind using RTTI (or Boost.TypeIndex, which doesn't require RTTI), you can use type erasure and void* to get what you want. It's a bit dirty, but it does the job.
#include <functional>
#include <typeindex>
#include <unordered_map>
class driver {
public:
template <typename Container, typename Populator>
void register_populator(Populator populator) {
populators[type_index<Container>()] = [populator](void* v) {
Container& container = *static_cast<Container*>(v);
populator(container);
};
}
template <typename Container>
void execute(Container& container) {
auto it = populators.find(type_index<Container>());
if (it != populators.end()) {
it->second(&container);
}
}
private:
template <typename T>
static std::type_index type_index() {
return std::type_index(typeid(std::remove_cv_t<T>));
}
std::unordered_map<std::type_index, std::function<void (void*)>> populators;
};
Then use like so:
#include <vector>
#include <iostream>
int main() {
driver d;
d.register_populator<std::vector<int>>([](std::vector<int>&) {
std::cout << "Populate vector<int>\n";
});
d.register_populator<std::vector<float>>([](std::vector<float>&) {
std::cout << "Populate vector<float>\n";
});
std::vector<int> int_vector;
std::vector<float> float_vector;
d.execute(int_vector);
d.execute(float_vector);
}
Output:
Populate vector<int>
Populate vector<float>
Related
Suppose I have this class :
class Component1;
class Component2;
// many different Components
class Component42;
class MyClass
{
public:
MyClass(void) {};
std::list<Component1> component1List;
std::list<Component2> component2List;
// one list by component
std::list<Component42> component42List;
};
I would like to create a function with the following signature:
template<class T> void addElement(T component);
It should do the following:
if component is of type Component1, add it to Component1List
if component is of type Component2, add it to Component2List, etc.
Is it possible? What's a good way to do this?
I can obtain the same behaviour with a function like :
template<class T> void addElement(int componentType, T component);
but I'd rather not have to specify the componentType like this : it's useless information and it open the door to possible errors (if componentType doesn't represent the type of component).
std::tuple to the rescue.
changelog:
use std::decay_t
added the variadic argument form
add_component() now returns a reference to this to allow call-chaining.
#include <iostream>
#include <list>
#include <utility>
#include <type_traits>
#include <tuple>
class Component1 {};
class Component2 {};
struct Component3 {
Component3() {}
};
// many different Components
template<class...ComponentTypes>
class MyClassImpl
{
template<class Component> using list_of = std::list<Component>;
public:
using all_lists_type =
std::tuple<
list_of<ComponentTypes> ...
>;
// add a single component
template<class Component>
MyClassImpl& add_component(Component&& c)
{
list_for<Component>().push_back(std::forward<Component>(c));
return *this;
}
// add any number of components
template<class...Components>
MyClassImpl& add_components(Components&&... c)
{
using expand = int[];
void(expand { 0, (void(add_component(std::forward<Components>(c))), 0)... });
return *this;
}
template<class Component>
auto& list_for()
{
using component_type = std::decay_t<Component>;
return std::get<list_of<component_type>>(_lists);
}
template<class Component>
const auto& list_for() const
{
using component_type = std::decay_t<Component>;
return std::get<list_of<component_type>>(_lists);
}
private:
all_lists_type _lists;
};
using MyClass = MyClassImpl<Component1, Component2, Component3>;
int main()
{
MyClass c;
c.add_component(Component1());
c.add_component(Component2());
const Component3 c3;
c.add_component(c3);
c.add_components(Component1(),
Component2(),
Component3()).add_components(Component3()).add_components(Component1(),
Component2());
std::cout << c.list_for<Component1>().size() << std::endl;
return 0;
}
The most straightforward variant is to simply not use templates but to overload the addElement() function:
void addElement(Component1 element)
{
this->element1List.push_back(element);
}
void addElement(Component2 element)
{
this->element2List.push_back(element);
}
// ... etc
However, this might get tedious if you have many of these (and you don't just have addElement(), I guess). Using a macro to generate the code for each type could still do the job with reasonable effort.
If you really want to use templates, you could use a template function and specialize the template function for each type. Still, this doesn't reduce the amount of code repetition when compared with the above approach. Also, you could still reduce it using macros to generate the code.
However, there's hope for doing this in a generic way. Firstly, let's create a type that holds the list:
template<typename T>
struct ComponentContainer
{
list<T> componentList;
};
Now, the derived class just inherits from this class and uses C++ type system to locate the correct container baseclass:
class MyClass:
ComponentContainer<Component1>,
ComponentContainer<Component2>,
ComponentContainer<Component3>
{
public:
template<typename T>
void addElement(T value)
{
ComponentContainer<T>& container = *this;
container.componentList.push_back(value);
}
}
Notes here:
This uses private inheritance, which is very similar to the containment you originally used.
Even though ComponentContainer is a baseclass, it doesn't have any virtual functions and not even a virtual destructor. Yes, this is dangerous and should be documented clearly. I wouldn't add a virtual destructor though, because of the overhead it has and because it shouldn't be needed.
You could drop the intermediate container altogether and derive from list<T>, too. I didn't because it will make all of list's memberfunctions available in class MyClass (even if not publicly), which might be confusing.
You can't put the addElement() function into the base class template to avoid the template in the derived class. The simple reason is that the different baseclasses are scanned in order for a addElement() function and only then overload resolution is performed. The compiler will only find the addElement() in the first baseclass therefore.
This is a plain C++98 solution, for C++11 I'd look at the type-based tuple lookup solutions suggested by Jens and Richard.
If there are not too many classes you could go with overloading. A template-based solution could be done with type-based lookup for tuples:
class MyClass {
public:
template<typename T> void addElement(T&& x) {
auto& l = std::get<std::list<T>>(lists);
l.insert( std::forward<T>(x) );
}
private:
std::tuple< std::list<Component1>, std::list<Component2> > lists;
};
If you don't know in advance the types you will need storing when instantiating the multi-container an option is to hide the types and using type_index to keep a map of lists:
struct Container {
struct Entry {
void *list;
std::function<void *(void*)> copier;
std::function<void(void *)> deleter;
};
std::map<std::type_index, Entry> entries;
template<typename T>
std::list<T>& list() {
Entry& e = entries[std::type_index(typeid(T))];
if (!e.list) {
e.list = new std::list<T>;
e.deleter = [](void *list){ delete ((std::list<T> *)list); };
e.copier = [](void *list){ return new std::list<T>(*((std::list<T> *)list)); };
}
return *((std::list<T> *)e.list);
}
~Container() {
for (auto& i : entries) i.second.deleter(i.second.list);
}
Container(const Container& other) {
// Not exception safe... se note
for (auto& i : other.entries) {
entries[i.first] = { i.second.copier(i.second.list),
i.second.copier,
i.second.deleter };
}
};
void swap(Container& other) { std::swap(entries, other.entries); }
Container& operator=(const Container& other) {
Container(other).swap(*this);
return *this;
};
Container() { }
};
that can be used as:
Container c;
c.list<int>().push_back(10);
c.list<int>().push_back(20);
c.list<double>().push_back(3.14);
NOTE: the copy constructor as written now is not exception safe because in case a copier throws (because of an out of memory or because a copy constructor of an element inside a list throws) the already allocated lists will not be deallocated.
void addElement(Component1 component) {
componentList1.insert(component);
}
void addElement(Component2 component) {
componentList2.insert(component);
}
First, I really like the pattern of lazy initialization of singletons. I use it in the following way to get different kind of data with varying value types (The example is simplified):
class A
{
template<typename T>
const T& getData() const
{
static T data;
return data;
}
}
I know that the data variable is not connected to any instances of the class and that it exists until the program ends.
But what I want now, is that each instance of the class A should hold the variables in a non-static way and still there should be the flexibility of calling .getData<bool>() or with any other data type, without the need to specify each possible data type in the class definition.
Is that possible? I have not come up with an idea to implement that.
I thought of something with a container like:
template<A*, typename T>
class DataContainer
{
T data;
}
With that one can extend the code to:
class A
{
template<typename T>
const T& getData() const
{
static DataContainer<this, T> container;
return container.data;
}
}
But that does not compile.
Does anybody of you have an idea how to implement that?
Here's one idea, using Boost.any:
#include <typeinfo>
#include <type_index>
#include <unordered_map>
#include <boost/any.hpp>
struct ThingGetter
{
template <typename T>
T & get()
{
auto key = std::type_index(typeid(T));
auto it = things.find(key);
if (it == things.end())
{
it = things.emplace(key, boost::any(T())).first;
}
return boost::any_cast<T&>(*it);
}
std::unordered_map<std::type_index, boost::any> things;
};
This simple version assumes that each type can be value-initialized and creates a value-initialized value if no entry for the requested type exists. Alternative implementations could return a pointer that might be null and have a separate insertion interface.
Usage:
ThingGetter mythings;
mythings.get<bool>() = true;
mythings.get<double>() = 1.5;
return mythings.get<int>();
I'm trying to implement a superclass in c++ that would implement one method:
-void setValueForKey(void *value, string key);
all this method would have to do is to set the value of a property associated with a given key to the new value.
This would be easy in a language that implements introspection mechanisms; as far as I know C++ doesn't.
In order to accomplish this I created another method:
void registerKeyForProperty(void *propertyPtr, string key);
all this method does is it stores in and internal map a pointer to a property associated with a given key, so all my subclasses would call this for every property they declare and I would have a way of setting values for properties without necessity to use the setters.(That's what I need!) (I explain why at the end of the post...)
for this second function I have the following implementation:
void registerKeyForProperty(void *propertyPtr, string key){
_keysDictionary->insert(pair<string,void*>(key,property));
}
where _keysDictionary is a stl map.
for the first one I have the following implementation:
void ConstructableObject::setValueForKey(void* value, string key) {
map<string,void *>::iterator it=_keysDictionary->find(key);
if(it==_keysDictionary->end()){return;}//just return if there is nothing for that key
void *property=it->second;
(*property)=value;
}
the problem is the last line is not legal C++ because ofcourse I cannot just deference that void*.
My questions are:
Is there any other way of implementing the desired functionality?
Is there a "legal" way of doing this the way I am doing it? (I cannot simply use a reinterpret_cast cause I don't know what to cast to...)
Why this:
I need to parse and xml file that has some information about some objects. I'll be using TinyXML and therefore I'll have the atribute names for the objects and their values. That would be how I would like to use it:
MyClass obj();//the constructor would call setValueForKey(...,...) for every property so all are now registered
for every attribute{
obj.setValueForKey(attribute.value,attribute.name);
}
//all properties should be set now
If the key exists, why not simply do
_keysDictionary[key] = value;
Or if you want to use the iterator
it->second = value;
It could be done with using of the type awareness techniques and for example The Memento Pattern is one of choices. The following code could be extended with the some macro stuff that generating the unique keys based on the attribute pointer signature:
class introspection
{
public:
template <typename Class, typename Member>
void registerKey(std::string key, Member Class::*memberPointee, Class* classPointee)
{
typedef member_setter<Class, Member> hold_member_pointer;
base_setter* setter = new hold_member_pointer(memberPointee, classPointee);
keys.insert(std::make_pair(key, setter));
}
template <typename Value>
void setValue(std::string key, Value value)
{
if ( keys.count(key) > 0 )
{
keys[key]->set(value);
}
else
{
throw std::logic_error("no such key");
}
}
private:
struct base_setter
{
virtual void set(boost::any value) = 0;
}; // struct base_setter
template <typename Class, typename Member>
struct member_setter : base_setter
{
member_setter(Member Class::*memberPointee, Class* classPointee)
: memberPointee(memberPointee)
, classPointee(classPointee) {}
void set(boost::any value) override
{
Member newValue = boost::any_cast<Member>(value);
classPointee->*memberPointee = newValue;
}
Member Class::*memberPointee;
Class* classPointee;
}; // struct member_setter
std::map<std::string, base_setter*> keys;
}; // class introspection
struct Data
{
int value;
}; // struct Data
int main()
{
introspection i;
Data d;
d.value = 100;
i.registerKey("value", &Data::value, &d);
i.setValue("value", 200); // OK
i.setValue("value", "not valid"); // bad_any_cast
}
The one thing that could be (not so easily) improved here is provide the compile-time type check for setValue, instead of runtime any_cast casting.
Make setValueForKey be a templated function instead of a function that accepts a void pointer. That way, you can know about the type information of the property long enough to create a templated setter
class BaseSetter
{
public:
virtual void set(void* inValue) = 0;
}
template <typename T>
class SpecializedSetter
{
public:
SpecializedSetter(T* inMyValue)
: mValue(inValue)
{ }
virtual void set(void* inValue)
{
*mValue = *reinterpret_cast<T*>(inValue);
}
private:
T* mValue;
}
template <typename T>
void registerKeyForProperty(T* inValue, string inKey)
{
registerSetterForProperty(new SpecificSetter<T>(inValue), inKey);
}
This, however, assumes inValue is a pointer to the same type of data as the value on the class. To make that safe, consider boost::any or defining some other type which contains the type information from the XML file and using that rather than void*
I have a class which has a template:
template<class T = int> class slider;
The class has a void Process(void) method, so, I think it should be callable regarless of the type, return value is void and there are no parameters to it.
As for now I have this code to call process each frame in my application:
//class menu:
typedef boost::variant<std::shared_ptr<slider<int>>,std::shared_ptr<slider<float>>,std::shared_ptr<slider<double>>,std::shared_ptr<slider<char>>> slider_type;
std::map<std::string,slider_type> Sliders;
//buttons ... etc ...
void Process()
{
if(!Sliders.empty())
{
for(auto i = Sliders.begin(); i != Sliders.end(); ++i)
{
switch(i->second.which())
{
case 0://slider<int>
{
boost::get<std::shared_ptr<slider<int>>>(i->second)->Process();
break;
}
case 1://slider<float>
{
boost::get<std::shared_ptr<slider<float>>>(i->second)->Process();
break;
}
//.....
}
}
}
}
Is it possible to execute the functions Process() like in the following example?
for(auto i = Sliders.begin(); i != Sliders.end(); ++i)
{
switch(i->second.which())
{
boost::get<???Any???>(i->second)->Process();
}
}
If yes, how?
What would such a function return? You can't change the type of a function at runtime. And the point of a variant is that it's contents are determined at runtime.
The only thing it could return is a boost::any. Which is really just exchanging one kind of unknown for another (an unknown that's a lot harder to deal with when you don't know what it contains, mind you). But if you want to see such a visitor:
struct convert_to_any : public boost::static_visitor<boost::any>
{
template<typename T> boost::any operator() (const T& t) {return t;}
};
Use apply_visitor on that, and you will get an any back. Though I fail to see how that's helpful.
In any case, if you're using get on a variant, you are almost certainly doing the wrong thing. The correct way to access the elements of a variant is with a visitor, not with get.
In your case, the visitor should be simple:
struct ProcessVisitor : public boost::static_visitor<>
{
template<typename T> void operator() (const T& t) const {t->Process();}
};
Just use apply_visitor on that. If the variant contains a type that can be used with operator-> and the return value of that function can have Process called on it, then it will.
(Untested code!)
struct CallProcess : static_visitor<>
{
template <class T>
void operator()(const T &t) const
{
t->Process();
}
};
for(auto i = Sliders.begin(); i != Sliders.end(); ++i)
{
boost::apply_visitor(CallProcess(), i->second);
}
No, not at all. You have to visit and deal with the case of every type. That is much better done with a visitor than your switch hack.
It's not possible because boost::variant has no way to know that all the types in the variant have anything in common. In fact, since the compiler generates a distinct class for each template specialization used, the address of the Process() function that would need to be used is different for each type in the boost::variant. To get around this you could abandon variant and use virtual functions and polymorphic classes sharing a common base class.
I have a map which represents a configuration. It's a map of std::string and boost::any.
This map is initialized at the start and I'd like the user to be able to override these options on the command line.
What I'd love to do is build the program options from this map using the options_description::add_option() method. However, it takes a template argument po::value<> whereas all I have is boost::any.
So far, I just have the shell of the code. m_Config represents my configuration class, and getTuples() returns a std::map<std::string, Tuple>. TuplePair is a typedef of std::pair<std::string, Tuple> and the Tuple contains the boost::any I am interested in.
po::options_description desc;
std::for_each(m_Config.getTuples().begin(),
m_Config.getTuples().end(),
[&desc](const TuplePair& _pair)
{
// what goes here? :)
// desc.add_options() ( _pair.first, po::value<???>, "");
});
Is there a way to build it this way, or do I need to resort to doing it myself?
Thanks in advance!
boost::any is not applicable to your problem. It performs the most basic form of type erasure: storage and (type-safe) retrieval, and that's it. As you've seen, no other operations can be performed. As jhasse points out, you could just test every type you want to support, but this is a maintenance nightmare.
Better would be to expand upon the idea boost::any uses. Unfortunately this requires a bit of boiler-plate code. If you'd like to try it, there's a new Boost library being discussed right now on the mailing list (titled "[boost] RFC: type erasure") that is essentially a generalized type erasure utility: you define the operations you'd like your erased type to support, and it generates the proper utility type. (It can simulate boost::any, for example, by requiring the erased type be copy-constructible and type-safe, and can simulate boost::function<> by additionally requiring the type be callable.)
Aside from that, though, your best option is probably to write such a type yourself. I'll do it for you:
#include <boost/program_options.hpp>
#include <typeinfo>
#include <stdexcept>
namespace po = boost::program_options;
class any_option
{
public:
any_option() :
mContent(0) // no content
{}
template <typename T>
any_option(const T& value) :
mContent(new holder<T>(value))
{
// above is where the erasure happens,
// holder<T> inherits from our non-template
// base class, which will make virtual calls
// to the actual implementation; see below
}
any_option(const any_option& other) :
mContent(other.empty() ? 0 : other.mContent->clone())
{
// note we need an explicit clone method to copy,
// since with an erased type it's impossible
}
any_option& operator=(any_option other)
{
// copy-and-swap idiom is short and sweet
swap(*this, other);
return *this;
}
~any_option()
{
// delete our content when we're done
delete mContent;
}
bool empty() const
{
return !mContent;
}
friend void swap(any_option& first, any_option& second)
{
std::swap(first.mContent, second.mContent);
}
// now we define the interface we'd like to support through erasure:
// getting the data out if we know the type will be useful,
// just like boost::any. (defined as friend free-function)
template <typename T>
friend T* any_option_cast(any_option*);
// and the ability to query the type
const std::type_info& type() const
{
return mContent->type(); // call actual function
}
// we also want to be able to call options_description::add_option(),
// so we add a function that will do so (through a virtual call)
void add_option(po::options_description desc, const char* name)
{
mContent->add_option(desc, name); // call actual function
}
private:
// done with the interface, now we define the non-template base class,
// which has virtual functions where we need type-erased functionality
class placeholder
{
public:
virtual ~placeholder()
{
// allow deletion through base with virtual destructor
}
// the interface needed to support any_option operations:
// need to be able to clone the stored value
virtual placeholder* clone() const = 0;
// need to be able to test the stored type, for safe casts
virtual const std::type_info& type() const = 0;
// and need to be able to perform add_option with type info
virtual void add_option(po::options_description desc,
const char* name) = 0;
};
// and the template derived class, which will support the interface
template <typename T>
class holder : public placeholder
{
public:
holder(const T& value) :
mValue(value)
{}
// implement the required interface:
placeholder* clone() const
{
return new holder<T>(mValue);
}
const std::type_info& type() const
{
return typeid(mValue);
}
void add_option(po::options_description desc, const char* name)
{
desc.add_options()(name, po::value<T>(), "");
}
// finally, we have a direct value accessor
T& value()
{
return mValue;
}
private:
T mValue;
// noncopyable, use cloning interface
holder(const holder&);
holder& operator=(const holder&);
};
// finally, we store a pointer to the base class
placeholder* mContent;
};
class bad_any_option_cast :
public std::bad_cast
{
public:
const char* what() const throw()
{
return "bad_any_option_cast: failed conversion";
}
};
template <typename T>
T* any_option_cast(any_option* anyOption)
{
typedef any_option::holder<T> holder;
return anyOption.type() == typeid(T) ?
&static_cast<holder*>(anyOption.mContent)->value() : 0;
}
template <typename T>
const T* any_option_cast(const any_option* anyOption)
{
// none of the operations in non-const any_option_cast
// are mutating, so this is safe and simple (constness
// is restored to the return value automatically)
return any_option_cast<T>(const_cast<any_option*>(anyOption));
}
template <typename T>
T& any_option_cast(any_option& anyOption)
{
T* result = any_option_cast(&anyOption);
if (!result)
throw bad_any_option_cast();
return *result;
}
template <typename T>
const T& any_option_cast(const any_option& anyOption)
{
return any_option_cast<T>(const_cast<any_option&>(anyOption));
}
// NOTE: My casting operator has slightly different use than
// that of boost::any. Namely, it automatically returns a reference
// to the stored value, so you don't need to (and cannot) specify it.
// If you liked the old way, feel free to peek into their source.
#include <boost/foreach.hpp>
#include <map>
int main()
{
// (it's a good exercise to step through this with
// a debugger to see how it all comes together)
typedef std::map<std::string, any_option> map_type;
typedef map_type::value_type pair_type;
map_type m;
m.insert(std::make_pair("int", any_option(5)));
m.insert(std::make_pair("double", any_option(3.14)));
po::options_description desc;
BOOST_FOREACH(pair_type& pair, m)
{
pair.second.add_option(desc, pair.first.c_str());
}
// etc.
}
Let me know if something is unclear. :)
template<class T>
bool any_is(const boost::any& a)
{
try
{
boost::any_cast<const T&>(a);
return true;
}
catch(boost::bad_any_cast&)
{
return false;
}
}
// ...
po::options_description desc;
std::for_each(m_Config.getTuples().begin(),
m_Config.getTuples().end(),
[&desc](const TuplePair& _pair)
{
if(any_is<int>(_pair.first))
{
desc.add_options() { _pair.first, po::value<int>, ""};
}
else if(any_is<std::string>(_pair.first))
{
desc.add_options() { _pair.first, po::value<std::string>, ""};
}
else
{
// ...
}
});
// ...
If you have more than a handful of types consider using typelists.