Calling a parameterized method for list items with different template parameters

I am trying to save and process a list of template class objects with different types of parameters; the template class has two parameterized methods: one returns the type of the parameter and void accepts it as an input.

In particular, I have a template class that is defined as follows:

template<typename T> class Test { public: virtual T a() = 0; virtual void b(T t) = 0; }; 

And various specifications, such as:

 class TestInt : public Test<int> { public: int a() { return 1; } void b(int t) { std::cout << t << std::endl; } }; class TestString : public Test<std::string> { public: std::string a() { return "test"; } void b(std::string t) { std::cout << t << std::endl; } }; 

I would like to be able to store different objects in the same list like TestInt and TestString and scroll through it calling one method as input to another, for example:

 for (auto it = list.begin(); it != list.end(); ++it) (*it)->b((*it)->a()); 

I looked at boost::any , but I can not pass the iterator to a specific class, because I do not know the specific parameter type for each saved object. It might not be possible to do this in a statically typed language like C ++, but I was wondering if there could be a way around it.

Just for completeness, I’ll add that my common goal is to develop a “parameterized observer”, namely the ability to define an observer (like the Observer template) with different parameters: the Test class is an observer class, and a list of different types of observers that I'm trying to correctly determine is stored in the class of the subject, which notifies them of all methods a() and b() .

+5
source share
2 answers

Virtual objects do not really matter, since the signatures are different for each T

So it looks like you have another version of the eternal “how can we emulate virtual function templates” or “how to create an interface without virtual functions”:

The first basically contains an idea that you could use here.

Here is the idea of ​​what I will do:

Live on coliru

 #include <algorithm> #include <iostream> namespace mytypes { template <typename T> struct Test { T a() const; void b(T t) { std::cout << t << std::endl; } }; template <> int Test<int>::a() const { return 1; } template <> std::string Test<std::string>::a() const { return "test"; } using TestInt = Test<int>; using TestString = Test<std::string>; } #include <boost/variant.hpp> namespace mytypes { using Value = boost::variant<int, std::string>; namespace detail { struct a_f : boost::static_visitor<Value> { template <typename T> Value operator()(Test<T> const& o) const { return oa(); } }; struct b_f : boost::static_visitor<> { template <typename T> void operator()(Test<T>& o, T const& v) const { ob(v); } template <typename T, typename V> void operator()(Test<T>&, V const&) const { throw std::runtime_error(std::string("type mismatch: ") + __PRETTY_FUNCTION__); } }; } template <typename O> Value a(O const& obj) { return boost::apply_visitor(detail::a_f{}, obj); } template <typename O, typename V> void b(O& obj, V const& v) { boost::apply_visitor(detail::b_f{}, obj, v); } } #include <vector> int main() { using namespace mytypes; using AnyTest = boost::variant<TestInt, TestString>; std::vector<AnyTest> list{TestInt(), TestString(), TestInt(), TestString()}; for (auto it = list.begin(); it != list.end(); ++it) b(*it, a(*it)); } 

Will print

 1 test 1 test 

Bonus Points

If you insist, you can wrap the AnyTest variant in the correct class and have the member functions a() and b(...) :

Live on coliru

 int main() { using namespace mytypes; std::vector<AnyTest> list{AnyTest(TestInt()), AnyTest(TestString()), AnyTest(TestInt()), AnyTest(TestString())}; for (auto it = list.begin(); it != list.end(); ++it) it->b(it->a()); } 
+2
source

Turning around in my comment above, the simplest thing I can think about now is to achieve what you are trying to do - at least as I understood it from your sample code, this is the following:

 /* Interface for your container, better not forget the destructor! */ struct Test { virtual void operate(void) = 0; virtual ~Test() {} }; /* Implementation hiding actual type */ template<typename T> struct TestImpl : public T, public Test { void operate(void) { T::b(T::a()); } }; /* Actual code as template policies */ struct IntTest { int a(void) { return 42; } void b(int value) { std::cout << value << std::endl; } }; struct StringTest { std::string a(void) { return "Life? Don't talk to me about life."; } void b(std::string value) { std::cout << value << std::endl; } }; 

Then you need to create a container for objects of the Test class and fill it with objects of the corresponding TestImpl<IntTest> , TestImpl<StringTest> , etc. To avoid cutting objects, you need a reference or a pointer to semantics, for example std::vector<std::unique_ptr<Test> > .

 for (auto it = list.begin(); it != list.end(); ++it) { (*it)->operate(); } 
+2
source

All Articles