Suppose I have a class in C++11 like this:
class Something
{
...
private:
class1* a;
class2* b;
class3* c;
public:
class1* reada() { return a; }
class2* readb() { return b; }
class3* readc() { return c; }
void customFunctionForclass1();
void customFunctionForclass2();
void customFunctionForclass3();
}
}
I'd like to make the read functions templated so that if another programmer adds another member class, the corresponding read function will be template-magic created.
Something like this maybe?
class Something
{
...
private:
templateContainer = {class1*,class2*,class3*}
template<thing in templateContainer>
thing variableOfTypeThing;
public:
template<thing in templateContainer>
<thing> read() {return variableOfTypeThing<thing>;}
void customFunctionForclass1();
void customFunctionForclass2();
void customFunctionForclass3();
}
As you can tell from the example, I'm confused.
Basically, I have a class which acts as a container for guaranteed unique class variables (no class1 A; class1 B)
Some function groups for the class are almost identical some function groups are highly varied. It would be great for future people to only have to modify the different parts of the class and get the rest from the templates.
I thought maybe there would be a way by splitting this class up into lots of classes and stuffing them into an array of void pointers, but that seems unwise.
Suggestions?
I'd like to make the read functions templated so that if another programmer adds another member class, the corresponding read function will be template-magic created.
You could encapsulate the user defined classes in a thin wrapper class with a read() function that returns the contained instance. Adding a user defined class to Something would then be done by inheriting wrapper<user_defined_class>.
Basically, I have a class which acts as a container for guaranteed unique class variables
Inheriting this wrapper prevents you from including the same class twice so it could possibly be a way forward:
#include <iostream>
// the "thing" wrapper
template<typename T>
struct thing {
// forward construction arguments to the contained variable
template<class... Args>
thing(Args&&... args) : variable(std::forward<Args>(args)...) {}
// basic interface, const and non-const. I called it get() instead of read()
T const& get() const { return variable; }
T& get() { return variable; }
private:
T variable;
};
// a troublesome user defined class that is not default constructibe :-(
struct user_defined {
user_defined() = delete; // silly example really, but it's just to demonstrate
user_defined(const std::string& v) : str(v) {}
user_defined& operator=(const std::string& v) {
str = v;
return *this;
}
std::string const& say() const { return str; }
private:
std::string str;
};
std::ostream& operator<<(std::ostream& os, const user_defined& ud) {
return os << ud.say();
}
// ... and the "Something" class that inherits the wrapped types.
class Something : thing<int>,
thing<double>,
thing<user_defined>
{
public:
// add initial values for types that are not default constructible
Something(const std::string& val) : thing<user_defined>(val) {}
Something() : Something("") {} // default ctor
// access via derived class, const and non-const
template<typename T>
T const& get() const {
return thing<T>::get(); // get() from the correct base
}
template<typename T>
T& get() {
return thing<T>::get(); // get() from the correct base
}
};
void print(const Something& s) {
// using the const interface
std::cout << s.get<int>() << "\n";
std::cout << s.get<double>() << "\n";
std::cout << s.get<user_defined>() << "\n";
}
int main() {
Something foo;
// using the non-const interface to set
foo.get<int>() = 10;
foo.get<double>() = 3.14159;
foo.get<user_defined>() = "Hello world";
print(foo);
}
Edit: It doesn't fulfill the index part of your question though. You access it using the type you'd like to get() as a tag. You basically build a very rudimentary tuple I guess.
Code based on #Ted Lyngmo's answer:
#include <iostream>
#include <string>
template<typename T>
struct thing {
// forward construction arguments to the contained variable
template<class... Args>
thing(Args&&... args) : variable(std::forward<Args>(args)...) {}
// basic interface, const and non-const. I called it get() instead of read()
T const& get() const { return variable; }
T& get() { return variable; }
protected:
T variable;
};
template<typename ...Ts>
struct things : thing<Ts>... {
template<class... SubTs>
things(thing<SubTs>&&... ts) : thing<SubTs>(std::move(ts))... {}
// access via derived class, const and non-const
template<typename T>
T const& get() const {
return thing<T>::get(); // get() from the correct base
}
template<typename T>
T& get() {
return thing<T>::get(); // get() from the correct base
}
};
// a troublesome user defined class that is not default constructibe :-(
struct user_defined {
user_defined() = delete; // silly example really, but it's just to demonstrate
user_defined(const std::string& v) : str(v) {}
user_defined& operator=(const std::string& v) {
str = v;
return *this;
}
std::string const& say() const { return str; }
private:
std::string str;
};
struct non_default {
non_default() = delete;
non_default(int) {}
};
std::ostream& operator<<(std::ostream& os, const user_defined& ud) {
return os << ud.say();
}
// ... and the "Something" class that inherits the wrapped types.
class Something : public things<int, double, user_defined, non_default>
{
public:
// add initial values for types that are not default constructible
Something(const std::string& val) : things(thing<user_defined>(val), thing<non_default>(0)) {}
Something() : Something("") {} // default ctor
};
void print(const Something& s) {
// using the const interface
std::cout << s.get<int>() << "\n";
std::cout << s.get<double>() << "\n";
std::cout << s.get<user_defined>() << "\n";
}
int main() {
Something foo;
// using the non-const interface to set
foo.get<int>() = 10;
foo.get<double>() = 3.14159;
foo.get<user_defined>() = "Hello world";
print(foo);
}
Related
This question already has answers here:
What is a dangling reference? [duplicate]
(1 answer)
What is a dangling pointer?
(7 answers)
Closed 5 days ago.
I've been going over my code and fiddling but I can't seem to figure out why I'm not getting the expected output but instead random symbols.
The expected output is: JoeUPSReminderPick up your package!
54.23
I get the < but anything after that is gibberish. Any help would be appreciated.
#include <cstddef> // for std::size_t
#include <iostream>
#include <memory>
#include <ostream>
#include <string>
#include <utility> // for std::move, std::forward
#include <vector>
class xml_node_base
{
public:
virtual ~xml_node_base() = default;
void output(std::ostream& os) const
{
do_output_open(os);
do_output_body(os);
do_output_close(os);
}
protected:
virtual void do_output_open(std::ostream& os) const = 0; // abstract
virtual void do_output_body(std::ostream&) const { } // not abstract
virtual void do_output_close(std::ostream& os) const = 0; // abstract
};
using xml_node_base_t = std::shared_ptr<xml_node_base>;
using xml_node_bases_t = std::vector<xml_node_base_t>;
template <typename T, typename... Args>
inline xml_node_base_t make_xml_node(Args&&... args)
{
return std::make_shared<T>(std::forward<Args>(args)...);
}
class xml_node: virtual public xml_node_base
{
private:
std::string const& node_name;
public:
xml_node() = delete;
xml_node(std::string const& name) : node_name(name)
{
};
protected:
void do_output_open(std::ostream& os) const override
{
os << "<" << node_name << ">";
};
void do_output_close(std::ostream& os) const override
{
os << "</" << node_name << ">";
};
};
class xml_node_with_children: public xml_node
{
private:
xml_node_bases_t children_;
public:
xml_node_with_children() = delete;
xml_node_with_children(std::string const& name) : xml_node(name)
{
};
xml_node_with_children(std::string const& name, std::size_t reserve) : xml_node_with_children(name)
{
children_.reserve(reserve);
};
xml_node_with_children(std::string const& name, xml_node_bases_t children) : xml_node(name), children_(std::move(children))
{
};
protected:
auto& children() { return children_; };
auto const& children() const { return children_; };
void do_output_body(std::ostream& os) const
{
for (auto const& c : children_)
{
c -> output(os);
}
};
};
template <typename T>
class value_node : public xml_node
{
private:
T datum;
protected:
void do_output_body(std::ostream& os) const
{
os << datum;
}
public:
value_node(std::string const& name, T const& v) : xml_node(name), datum(v)
{
}
};
class note : public xml_node_with_children
{
public:
note() = delete;
note(std::string const& to, std::string const& from, std::string const& subject, std::string const& message) : xml_node_with_children("note", 4)
{
children().push_back(make_xml_node<value_node<std::string>>("to",to));
children().push_back(make_xml_node<value_node<std::string>>("from",from));
children().push_back(make_xml_node<value_node<std::string>>("subject",subject));
children().push_back(make_xml_node<value_node<std::string>>("message",message));
}
};
class root : protected xml_node_with_children
{
public:
using xml_node_with_children::xml_node_with_children;
using xml_node_with_children::output;
using xml_node_with_children::children;
};
std::ostream& operator<<(std::ostream& os, root const& r)
{
r.output(os);
return os;
}
int main()
{
root notes{"notes"};
notes.children().push_back(
make_xml_node<note>("Joe", "UPS", "Reminder", "Pick up your package!")
);
notes.children().push_back(
make_xml_node<value_node<double>>("priority",54.23)
);
std::cout << notes << '\n';
}
I think the problem could be with the for loop on line 90, since I'm not too familiar with the -> operator.
std::string const& node_name;
xml_node(std::string const& name) : node_name(name)
This class member is a reference, and the constructor initializes it from a reference that gets passed in as a parameter to the constructor.
Let's trace things all the way back and see where the parameter, to the constructor, originally comes from. Here's one example:
children().push_back(make_xml_node<value_node<std::string>>("to",to));
The parameter is a literal string, "to".
C++ is very famous, and is very well known for giving everyone every opportunity to shoot themself in the foot, if that's what they really want to do, so:
A temporary std::string object gets constructed.
A reference to this object gets passed as a parameter, through several onion layers of constructors, elephant-style.
A reference to this object gets saved in a member of the base class.
After all the constructors finish, and this statement finishes executing, the temporary std::string object, that owns this "to" gets destroyed.
The instance of the class now has a reference to a destroyed object, in its node_name.
This is repeated for all the other objects in the shown code that get constructed like that.
You just shot yourself in the foot.
Hi Stack Overflow Community !
I am working on a project that heavily uses the interesting nlohmann_json library and it appears that I need to add an inheritance link on a specific class, which objects are serialized at one moment.
I tried different advice found on the github Issues page of the library, but can't make it work.
Here is an dummy code I tried :
#include <nlohmann/json.hpp>
#include <iostream>
#include <memory>
#include <vector>
using json = nlohmann::json;
namespace nlohmann {
template <typename T>
struct adl_serializer<std::unique_ptr<T>> {
static void to_json(json& j, const std::unique_ptr<T>& opt) {
if (opt) {
j = *opt.get();
} else {
j = nullptr;
}
}
};
}
class Base {
public:
Base() = default;
virtual ~Base() = default;
virtual void foo() const { std::cout << "Base::foo()" << std::endl; }
};
class Obj : public Base
{
public:
Obj(int i) : _i(i) {}
void foo() const override { std::cout << "Obj::foo()" << std::endl; }
int _i = 0;
friend std::ostream& operator<<(std::ostream& os, const Obj& o);
};
std::ostream& operator<<(std::ostream& os, const Base& o)
{
os << "Base{} ";
return os;
}
std::ostream& operator<<(std::ostream& os, const Obj& o)
{
os << "Obj{"<< o._i <<"} ";
return os;
}
void to_json(json& j, const Base& b)
{
std::cout << "called to_json for Base" << std::endl;
}
void to_json(json& j, const Obj& o)
{
std::cout << "called to_json for Obj" << std::endl;
}
int main()
{
std::vector<std::unique_ptr<Base>> v;
v.push_back(std::make_unique<Base>());
v.push_back(std::make_unique<Obj>(5));
v.push_back(std::make_unique<Base>());
v.push_back(std::make_unique<Obj>(10));
std::cout << v.size() << std::endl;
json j = v;
}
// Results in :
// Program returned: 0
// 4
// called to_json for Base
// called to_json for Base
// called to_json for Base
// called to_json for Base
(https://gcc.godbolt.org/z/dc8h8f)
I understand that the adl_serializer only get the type Base when called, but I don't see how to make him aware of the type Obj as well...
Does anyone see what I am missing here ?
Thanks in advance for your advice and help !
nlohmann.json does not include polymorphic serializing, but you can implement it yourself in a specialized adl_serializer. Here we're storing and checking an additional _type JSON field, used as a key to map to pairs of type-erased from/to functions for each derived type.
namespace PolymorphicJsonSerializer_impl {
template <class Base>
struct Serializer {
void (*to_json)(json &j, Base const &o);
void (*from_json)(json const &j, Base &o);
};
template <class Base, class Derived>
Serializer<Base> serializerFor() {
return {
[](json &j, Base const &o) {
return to_json(j, static_cast<Derived const &>(o));
},
[](json const &j, Base &o) {
return from_json(j, static_cast<Derived &>(o));
}
};
}
}
template <class Base>
struct PolymorphicJsonSerializer {
// Maps typeid(x).name() to the from/to serialization functions
static inline std::unordered_map<
char const *,
PolymorphicJsonSerializer_impl::Serializer<Base>
> _serializers;
template <class... Derived>
static void register_types() {
(_serializers.emplace(
typeid(Derived).name(),
PolymorphicJsonSerializer_impl::serializerFor<Base, Derived>()
), ...);
}
static void to_json(json &j, Base const &o) {
char const *typeName = typeid(o).name();
_serializers.at(typeName).to_json(j, o);
j["_type"] = typeName;
}
static void from_json(json const &j, Base &o) {
_serializers.at(j.at("_type").get<std::string>().c_str()).from_json(j, o);
}
};
Usage:
// Register the polymorphic serializer for objects derived from `Base`
namespace nlohmann {
template <>
struct adl_serializer<Base>
: PolymorphicJsonSerializer<Base> { };
}
// Implement `Base`'s from/to functions
void to_json(json &, Base const &) { /* ... */ }
void from_json(json const &, Base &) { /* ... */ }
// Later, implement `Obj`'s from/to functions
void to_json(json &, Obj const &) { /* ... */ }
void from_json(json const &, Obj &) { /* ... */ }
// Before any serializing/deserializing of objects derived from `Base`, call the registering function for all known types.
PolymorphicJsonSerializer<Base>::register_types<Base, Obj>();
// Works!
json j = v;
Caveats:
typeid(o).name() is unique in practice, but is not guaranteed to be by the standard. If this is an issue, it can be replaced with any persistent runtime type identification method.
Error handling has been left out, though _serializers.at() will throw std::out_of_range when trying to serialize an unknown type.
This implementation requires that the Base type implements its serialization with ADL from/to functions, since it takes over nlohmann::adl_serializer<Base>.
See it live on Wandbox
Assume I have a base class like this:
template<typename T>
class Base {
public:
Base& operator()(const T& value) {
this->value = value;
return *this;
}
T value;
};
Now I want to inherit from this class to create type-specific classes
class InheritedFloat : public Base<float> {} inheritedFloat;
Now here I try to catch this inheritance in a functon:
void function(const InheritedFloat& inherited) {
std::cout << inherited.value << '\n';
}
Calling this function like this works fine, of course:
int main() {
function(inheritedFloat); //(inheritedFloat is a global instance)
return 0;
}
But when I try to call it with the operator()(const float& value){...} member function, function(const InheritedFloat& inherited){...} doesn't see it as a InheritedFloat-Type but instead as a Base<float>-Type:
int main() {
function(inheritedFloat(10.f)); //error
return 0;
}
Error:
Error C2664 'void function(const InheritedFloat &)': cannot convert argument 1 from 'Base<float>' to 'const InheritedFloat &'
So how can I make operator()(const T& value){...} return InheritedFloat& instead of Base<float>&?
To clearify further, this is just a simplified example (of course). I have dozens of inheritance cases. So I can't just template-specify function()
template<typename T>
void function(const Base<T>& inherited) {
std::cout << inherited.value << '\n';
}
because each inheritance needs to be treated differently. Types will overlap, so there will be multiple Base<std::size_t> cases, for example.
The whole code:
#include <iostream>
template<typename T>
class Base {
public:
Base& operator()(const T& value) {
this->value = value;
return *this;
}
T value;
};
class InheritedFloat : public Base<float> {} inheritedFloat;
void function(const InheritedFloat& inherited) {
std::cout << inherited.value << '\n';
}
int main() {
function(inheritedFloat(10.f));
return 0;
}
Thanks for reading, I appreciate any help!
You can utilize CRTP here. By supplying extra template parameter you can make base class function return a reference to a derived class:
#include <iostream>
template<typename Derived, typename T>
class Base {
public:
Derived & operator()(const T& value) {
this->value = value;
return *static_cast<Derived *>(this);
}
T value;
};
class InheritedFloat : public Base<InheritedFloat, float> {} inheritedFloat;
void function(const InheritedFloat& inherited) {
std::cout << inherited.value << '\n';
}
int main() {
function(inheritedFloat(10.f));
return 0;
}
online compiler
Consider this pseudo-snippet:
class SomeClass
{
public:
SomeClass()
{
if(true)
{
fooCall = [](auto a){ cout << a.sayHello(); };
}
else
{
fooCall = [](auto b){ cout << b.sayHello(); };
}
}
private:
template<typename T>
std::function<void(T)> fooCall;
};
What I want is a class member fooCall which stores a generic lambda, which in turn is assigned in the constructor.
The compiler complains that fooCall cannot be a templated data member.
Is there any simple solution on how i can store generic lambdas in a class?
There is no way you'll be able to choose between two generic lambdas at run-time, as you don't have a concrete signature to type-erase.
If you can make the decision at compile-time, you can templatize the class itself:
template <typename F>
class SomeClass
{
private:
F fooCall;
public:
SomeClass(F&& f) : fooCall{std::move(f)} { }
};
You can then create an helper function to deduce F:
auto makeSomeClassImpl(std::true_type)
{
auto l = [](auto a){ cout << a.sayHello(); };
return SomeClass<decltype(l)>{std::move(l)};
}
auto makeSomeClassImpl(std::false_type)
{
auto l = [](auto b){ cout << b.sayHello(); };
return SomeClass<decltype(l)>{std::move(l)};
}
template <bool B>
auto makeSomeClass()
{
return makeSomeClassImpl(std::bool_constant<B>{});
}
I was not able to store std::function<> as a generic lambda in the class directly as a member. What I was able to do was to specifically use one within the class's constructor. I'm not 100% sure if this is what the OP was trying to achieve but this is what I was able to compile, build & run with what I'm suspecting the OP was aiming for by the code they provided.
template<class>
class test {
public: // While testing I changed this to public access...
// Could not get object below to compile, build & run
/*template<class U = T>
static std::function<void(U)> fooCall;*/
public:
test();
};
template<class T>
test<T>::test() {
// This would not compile, build & run
// fooCall<T> = []( T t ) { std::cout << t.sayHello(); };
// Removed the variable within the class as a member and moved it here
// to local scope of the class's constructor
std::function<void(T)> fooCall = []( auto a ) { std::cout << a.sayHello(); };
T t; // created an instance of <Type T>
fooCall(t); // passed t into fooCall's constructor to invoke the call.
}
struct A {
std::string sayHello() { return "A say's Hello!\n"; }
};
struct B {
std::string sayHello() { return "B say's Hello!\n"; }
};
int main() {
// could not instantiate an object of SomeClass<T> with a member of
// a std::function<> type that is stored by a type of a generic lambda.
/*SomeClass<A> someA;
SomeClass<B> someB;
someA.foo();
someB.foo();*/
// Simply just used the object's constructors to invoke the locally stored lambda within the class's constructor.
test<A> a;
test<B> b;
std::cout << "\nPress any key & enter to quit." << std::endl;
char c;
std::cin >> c;
return 0;
}
With the appropriate headers the above as is should compile, build & run giving the output below (At least in MSVS 2017 on Windows 7 64bit did); I left comments where I ran into errors and tried multiple different techniques to achieve a working example, errors occurred as others suggested and I found even more while working with the above code. What I was able to compile, build and run came down to this simple bit of code here without the comments. I also added another simple class to show it will work with any type:
template<class>
class test {
public:
test();
};
template<class T>
test<T>::test() {
std::function<void( T )> fooCall = []( auto a ) { std::cout << a.sayHello(); };
T t;
fooCall( t );
}
struct A {
std::string sayHello() { return "A say's Hello!\n"; }
};
struct B {
std::string sayHello() { return "B say's Hello!\n"; }
};
struct C {
int sayHello() { return 100; }
};
int main() {
test<A> testA;
test<B> testB;
test<C> testC;
std::cout << "\nPress any key & enter to quit." << std::endl;
char c;
std::cin >> c;
return 0;
}
Output:
A say's Hello!
B say's Hello!
100
Press any key & enter to quit
I don't know if this will help the OP directly or indirectly or not but if it does or even if it doesn't it is still something that they may come back to and build off of.
you can simply use a template class or...
If you can get away with using c++17, you could make fooCall's type std::function<void(const std::any&)> and make a small wrapper for executing it.
method 1 : simply use a template class (C++14).
method 2 : seems to mimic the pseudo code exactly as the OP intended (C++17).
method 3 : is a bit simpler and easier to use than method 2 (C++17).
method 4 : allows us to change the value of fooCall (C++17).
required headers and test structures for the demo :
#include <any> //not required for method 1
#include <string>
#include <utility>
#include <iostream>
#include <functional>
struct typeA {
constexpr const char * sayHello() const { return "Hello from A\n"; }
};
struct typeB {
const std::string sayHello() const { return std::string(std::move("Hello from B\n")); }
};
method 1 :
template <typename T>
class C {
const std::function<void(const T&)> fooCall;
public:
C(): fooCall(std::move([](const T &a) { std::cout << a.sayHello(); })){}
void execFooCall(const T &arg) {
fooCall(arg);
}
};
int main (void) {
typeA A;
typeB B;
C<typeA> c1;
C<typeB> c2;
c1.execFooCall(A);
c2.execFooCall(B);
return 0;
}
method 2 :
bool is_true = true;
class C {
std::function<void(const std::any&)> fooCall;
public:
C() {
if (is_true)
fooCall = [](const std::any &a) { std::cout << std::any_cast<typeA>(a).sayHello(); };
else
fooCall = [](const std::any &a) { std::cout << std::any_cast<typeB>(a).sayHello(); };
}
template <typename T>
void execFooCall(const T &arg) {
fooCall(std::make_any<const T&>(arg));
}
};
int main (void) {
typeA A;
typeB B;
C c1;
is_true = false;
C c2;
c1.execFooCall(A);
c2.execFooCall(B);
return 0;
}
method 3 :
/*Note that this very closely resembles method 1. However, we're going to
build off of this method for method 4 using std::any*/
template <typename T>
class C {
const std::function<void(const std::any&)> fooCall;
public:
C() : fooCall(std::move([](const std::any &a) { std::cout << std::any_cast<T>(a).sayHello(); })) {}
void execFooCall(const T &arg) {
fooCall(std::make_any<const T&>(arg));
}
};
int main (void) {
typeA A;
typeB B;
C<typeA> c1;
C<typeB> c2;
c1.execFooCall(A);
c2.execFooCall(B);
return 0;
}
method 4 :
/*by setting fooCall outside of the constructor we can make C a regular class
instead of a templated one, this also complies with the rule of zero.
Now, we can change the value of fooCall whenever we want.
This will also allow us to do things like create a container that stores
a vector or map of functions that each take different parameter types*/
class C {
std::function<void(const std::any&)> fooCall; //could easily be replaced by a vector or map
public:
/*could easily adapt this to take a function as a parameter so we can change
the entire body of the function*/
template<typename T>
void setFooCall() {
fooCall = [](const std::any &a) { std::cout << std::any_cast<T>(a).sayHello(); };
}
template <typename T>
void execFooCall(const T &arg) {
fooCall(std::make_any<const T&>(arg));
}
};
int main (void) {
typeA A;
typeB B;
C c;
c.setFooCall<typeA>;
c.execFooCall(A);
c.setFooCall<typeB>;
c.execFooCall(B);
return 0;
}
Output from Any method
Hello from A
Hello from B
I have a finite amount of classes with the nearly-same implementation, the only different being the underlying type of data they manipulate:
class IntContainer
{
public:
void setData(int data);
int getData();
int _data;
};
class BoolContainer
{
public:
void setData(bool data);
bool getData();
bool _data;
};
class StringContainer
{
public:
void setData(std::string data);
std::string getData();
std::string _data;
};
// Etc. You get the idea.
I'd like to reduce the code duplication of these classes by using templates like so:
template<typename T>
class GenericContainer
{
public:
void setData(T data);
T getData();
T _data;
};
And specialization:
typedef GenericContainer<int> IntContainer;
typedef GenericContainer<bool> BoolContainer;
typedef GenericContainer<std::string> StringContainer;
This works well. But I'd also like to add an abstract base class to these specialized classes to be able to manipulate them in a generic way (eg. in a collection). The problem is this base class should have the getData and setData methods to be able to call them even without knowing the dynamic type of the object manipulated.
I would implement it with something like this:
class Base
{
public:
virtual void setData(??? data) = 0;
virtual ??? getData() = 0;
};
// Modify GenericContainer's definition like so
template<typename T>
class GenericContainer : Base { ... }
And use it somehow like that:
int main(int argc, char const *argv[])
{
IntContainer intc = IntContainer();
intc.setData(42);
std::cout << intc.getData() << std::endl;
BoolContainer boolc = BoolContainer();
boolc.setData(false);
std::cout << boolc.getData() << std::endl;
std::vector<Base> v;
v.push_back(intf);
v.push_back(boolf);
for (std::vector<Base>::iterator it = v.begin() ; it != v.end(); ++it)
std::cout << it->getData() << std::endl;
return 0;
}
The problem is I don't know how to write the Base methods prototypes as the type is unknow (and does not matter, the derived class implementation should be called at runtime based on the dynamic type of the object).
TL;DR: How to implement an abstract base class over several fully specialized templated classes ?
There is simply no way to do what you want.
The problem is, if this was allowed, the compiler would have to generate as many virtual methods in the base class as there are possible specializations of the template child class (ie. an infinity) which is not possible.
How about making base template too? Of course there is no way you can do something like
std::vector<Base> v;
v.push_back(intf);
v.push_back(boolf);
but the rest you can achieve with something simple as
template<typename T>
class Base
{
public:
virtual void setData(T data) = 0;
virtual T getData() = 0;
};
// Modify GenericContainer's definition like so
template<typename T>
class GenericContainer : Base<T> {
T d;
public:
virtual void setData(T data) {d = data;}
virtual T getData() { return d; }
};
You can use it in any way as long as types match.
IntContainer intc = IntContainer();
intc.setData(42);
std::cout << intc.getData() << std::endl;
BoolContainer boolc = BoolContainer();
boolc.setData(true);
std::cout << boolc.getData() << std::endl;
std::vector<IntContainer> v;
v.push_back(intc);
// v.push_back(boolc); No can't do.
This is a solution for any types of classes that can round-trip through a stringstream, and such conversion is the right way to convert between types. It isn't efficient at all:
struct BaseContainer {
protected:
boost::any data;
std::function< std::string( boost::any const& ) > toString;
virtual void setDataAny( boost::any x, std::function< std::string( boost::any const& ) > convert ) {
data = x;
toString = convert;
}
public:
virtual boost::any getDataAny() const {
return data;
}
template<typename T>
void setData( T const& t ) {
setDataAny( boost::any(t), []( boost::any const& a )->std::string {
std::string retval;
std::stringstream ss;
try
{
ss << boost::any_cast< T >(a);
ss >> retval;
return retval;
} catch(const boost::bad_any_cast &) {
return retval;
}
});
};
template<typename T>
struct TypedContainer:BaseContainer {
public:
T getData() const {
T retval;
try {
retval = boost::any_cast<T>(getDataAny());
return retval;
} catch(const boost::bad_any_cast &) {
std::string str = toString( getDataAny() );
std::stringstream ss;
ss << str;
ss >> retval;
return retval;
}
}
};
with fewer types, you could do something similar, so long as you have conversion functions between them.
Alternatively, if you like exceptions, you could throw.
Alternatively, you could use boost::variants, which do no conversions, but work from a finite list of types (they are basically tagged unions that support more types than C++03 lets union do, and with some nice semantics on assign/copy/etc).
Assuming you have some design flexibility, you can change your interface to accommodate this, although its not as efficient as an infinite virtual table
You can set values through construction, or >>
You can get values through <<
Your vector needs to be a base pointer or reference, the size of each base object is variable, the pointer, explicit or implicit through a reference is of fixed size
Notice that copies are more efficient if the compiler knows that it is copying from one generic to another as opposed to base to base
#include <iostream>
#include <sstream>
#include <vector>
class gen_base
{
public:
virtual std::ostream & output(std::ostream& S) const = 0;
virtual std::istream & input(std::istream& S) = 0;
friend std::istream & operator >> (std::istream &S, gen_base &g) {
return g.input(S);
}
friend std::ostream & operator << (std::ostream &S, const gen_base &g) {
return g.output(S);
}
};
template<typename T>
class GenericContainer : public gen_base
{
public:
GenericContainer(T data) : _data(data) {}
GenericContainer(const gen_base& other) {
// std::cout << "EXPENSIVE" << std::endl;
std::stringstream cvt;
other.output(cvt);
input(cvt);
}
template <class U>
GenericContainer(const GenericContainer<U>& other)
{
// std::cout << "CHEAP" << std::endl;
_data=other.getData();
}
virtual std::istream & input(std::istream &S) {
return (S >> _data);
}
virtual std::ostream & output(std::ostream &S) const {
return (S << _data);
}
T getData() const {
return _data;
}
private:
T _data;
};
typedef GenericContainer<int> IntContainer;
typedef GenericContainer<bool> BoolContainer;
typedef GenericContainer<std::string> StringContainer;
int main(int argc, char const *argv[])
{
IntContainer * intc = new IntContainer(42);
std::cout << *intc << std::endl;
gen_base * boolc = new BoolContainer(*intc);
std::cout << *boolc << std::endl;
IntContainer * intc2 = new IntContainer(*boolc);
std::cout << *intc2 << std::endl;
std::vector<gen_base *> v; // has to be pointer to base;
v.push_back(intc);
v.push_back(boolc);
v.push_back(intc2);
for (std::vector<gen_base *>::iterator it = v.begin() ; it != v.end(); ++it)
std::cout << **it << std::endl;
delete intc;
delete boolc;
return 0;
}