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;
}
Related
I have some class that should be populated with values,
but I don't know the type of values.
To clarify, each vector in a class instance is populated with the same value types,
But one instance of SomeClass can have vector<int> and another vector<string> and so on.
How can I declare the vector as template, but not template the class itself?
template<typename T>
struct tvector {
typedef std::vector< std::vector<T> > type;
};
class SomeClass {
public:
int _someField;
tvector<T> _fieldValues; // this line fails to compile
};
Thanks in advance
There are a few different ways to shear this beast. It mostly depends on how you want to access this vector and how you determine its exact type at runtime.
std::variant
A variant can store a set of predetermined types. It's effective but also cumbersome if you have many different types because you have to funnel every access through some type checking.
class SomeClass {
public:
using variant_type = std::variant<
std::vector<int>, std::vector<double> >;
int _someField;
variant_type _fieldValues;
void print(std::ostream& stream) const
{
switch(_fieldValues.index()) {
case 0:
for(int i: std::get<0>(_fieldValues))
stream << i << ' ';
break;
case 1:
for(double i: std::get<1>(_fieldValues))
stream << i << ' ';
break;
default: break;
}
}
};
std::any
Any can hold literally any type. Improves extendability but makes working with the values hard.
class SomeClass {
public:
int _someField;
std::any _fieldValues;
void print(std::ostream& stream) const
{
if(_fieldValues.type() == typeid(std::vector<int>))
for(int i: std::any_cast<std::vector<int>>(_fieldValues))
stream << i << ' ';
else if(_fieldValues.type() == typeid(std::vector<double>))
for(double i: std::any_cast<std::vector<double>>(_fieldValues))
stream << i << ' ';
else
throw std::runtime_error("Not implemented");
}
};
Subclassing
The most elegant way (IMHO) is to use a templated subclass. Something like this:
class SomeClass {
public:
int _someField;
virtual ~SomeClass() = default;
virtual void print(std::ostream& stream) const = 0;
};
template<class T>
SomeClassT: public SomeClass
{
std::vector<T> _fieldValues;
public:
virtual void print(std::ostream& stream) const
{
for(const T& i: _fieldValues)
stream << i << ' ';
}
};
Or if you don't want to expose that part, make it a private member.
class SomeClassHelper {
public:
virtual ~SomeClassHelper() = default;
virtual void print(std::ostream& stream) const = 0;
};
template<class T>
SomeClassHelperT: public SomeClassHelper
{
std::vector<T> _fieldValues;
public:
virtual void print(std::ostream& stream) const
{
for(const T& i: _fieldValues)
stream << i << ' ';
}
};
class SomeClass {
public:
int _someField;
private:
std::unique_ptr<SomeClassHelper> helper;
public:
void print(std::ostream& stream) const
{ return helper->print(stream); }
};
I have an abstract class say A which is implemented by a template class say B, B is specialized for a vector type that implements a copy constructor and a copy assignment operator, but when I compile i get the error: static assertion failed: result type must be constructible from value type of input range. Declaring a virtual copy assignment in the abstract class does not help, as the signature there is: A & operator=(const A &); but in B it is implemented for type B so the signatures do not match.
Why I have this weird hierarchy?
Because I am trying to implement a json parsing library, I need to have a container that can store string, number, bigint, boolean and array types, so I implemented the hierarchy to achieve type erasure, the template type is for these 5 types and need to specialize only for string and vector types.
Actual hierarchy (minimal reproducible example):
#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <iostream>
class BasicJsonType {
public:
virtual std::string toString() const = 0;
virtual void setNull() = 0;
// copy assignment method
// virtual BasicJsonType& operator= (const BasicJsonType &value) = 0;
~BasicJsonType() = default;
};
template<typename T>
class BasicJsonTypeInterface: public BasicJsonType {
protected:
bool empty = true;
public:
virtual const T& get() = 0;
virtual void set(const T&) = 0;
};
namespace json {
// json::array is defined as
using array = std::vector<BasicJsonType>;
}
template <typename T>
class JsonValue {
T x;
public:
virtual std::string toString() {
return "";
}
virtual const T & get() {
return this->x;
}
virtual void set(const T &value) {
this->x = value;
}
};
template<>
class JsonValue<json::array>: public BasicJsonTypeInterface<json::array> {
std::shared_ptr<json::array> array;
public:
JsonValue() = delete;
JsonValue(const JsonValue<json::array> &value): JsonValue(*(value.array)) {
std::cout << "const JsonValue<json::array> &";
}
JsonValue(const json::array &array) {
std::cout << "const json::array &";
// error
this->array.reset(new json::array(array));
}
JsonValue(JsonValue<json::array> &&value): JsonValue(static_cast<json::array &&> (*(value.array)))
{ std::cout << "const JsonValue<json::array> &"; }
JsonValue(json::array &&array) {
this->array.reset(new json::array(std::move(array)));
this->empty = false;
}
virtual void setNull() { }
virtual const json::array &get() {
return *(this->array);
}
virtual void set(const json::array &value) {
this->array.reset(new json::array(value));
}
};
int main() {}
I created the interface type since I wanted to implement the get, set methods for all the types and irrespective of the type.
I searched for the error, and what I found is I am missing a copy function for the BasicJsonType, like what it suggests here.
There maybe some design flaws in this, since it is my first try with anything of practical use with c++, I am targeting for c++11.
std::vector<BasicJsonType>;
This is a useless type.
BasicJsonType is an abstract class. Abstract classes are not value types. std::vector stores value types.
std::vector expects regular types (or semiregular if you don't need copy). Abstract types are NOT regular types.
There are a number of ways to have polymorphic regular types, but they all take work or 3rd party libraries.
A minor issue:
~BasicJsonType() = default;
this should be virtual.
...
There are a number of ways to approach your problem of getting a regular type to store in std::vector.
Store unique_ptr<BasicJsonType> in your vector. This permits moving but not assignment*.
Implement a value_ptr (based off unique ptr) that understands how to (virtually) clone its contents when copied.
Implement a cow_ptr that uses a shared ptr under the hood for immutable data, and does a copy-on-write.
Create an any_with_interface based off std::any that guarantees the stored value matches an interface, and provides operator-> and * that returns that interface.
Store a std::variant of the various kinds of json concrete types. Write a helper function to get the abstract interface (if you need it).
As your set of supported types is closed (there are only so many json types), #5 is probably easiest.
class BasicJsonType {
public:
virtual std::string toString() const = 0;
virtual void setNull() = 0;
virtual bool isNull() const = 0;
protected: // no deleting through this interface
~BasicJsonType() = default;
};
// if we find this overload, remember to implement
// your own to_json_string for the type in question
template<class T>
std::string to_json_string( T const& ) = delete;
std::string to_json_string( std::string const& s ) { return s; }
std::string to_json_string( double const& d )
{
std::stringstream ss;
ss << d;
return ss.str();
}
template <typename T>
class JsonValue:public BasicJsonType {
public:
JsonValue() = default;
JsonValue(JsonValue const&) = default;
JsonValue(JsonValue &&) = default;
JsonValue& operator=(JsonValue const&) = default;
JsonValue& operator=(JsonValue &&) = default;
JsonValue( T t ):value(std::move(t)) {}
std::optional<T> value;
std::string toString() const final {
if (value)
return to_json_string(*value);
else
return "(null)";
}
bool isNull() const final {
return !static_cast<bool>(value);
}
void setNull() final {
value = std::nullopt;
}
};
template<class T>
JsonValue(T)->JsonValue<T>;
you create a free function to_json_string for each T you pass to JsonValue; if you don't, you get a compile-time error.
The remaining tricky part is making a variant containing a vector of a type depending on the same variant.
struct json_variant;
using json_array = std::vector<json_variant>;
struct json_variant :
std::variant< JsonValue<double>, JsonValue<std::string>, JsonValue<json_array> >
{
using std::variant< JsonValue<double>, JsonValue<std::string>, JsonValue<json_array> >::variant;
std::variant< JsonValue<double>, JsonValue<std::string>, JsonValue<json_array> > const& base() const { return *this; }
std::variant< JsonValue<double>, JsonValue<std::string>, JsonValue<json_array> >& base() { return *this; }
};
BasicJsonType const& getInterface( json_variant const& var )
{
return std::visit( [](auto& elem)->BasicJsonType const& { return elem; }, var.base());
}
BasicJsonType& getInterface( json_variant& var )
{
return std::visit( [](auto& elem)->BasicJsonType& { return elem; }, var.base());
}
std::string to_json_string( json_array const& arr )
{
std::stringstream ss;
ss << "{";
for (auto&& elem:arr)
{
ss << getInterface(elem).toString();
ss << ",";
}
ss << "}";
return ss.str();
}
and test code:
JsonValue<json_array> bob;
bob.value.emplace();
bob.value->push_back( JsonValue(3.14) );
bob.value->push_back( JsonValue(std::string("Hello world!")) );
std::cout << bob.toString();
there we go, a value-semantics Json data type in C++.
Live example.
In c++11, you can use boost::any or boost::variant. Everything I did above works with them, except the deduction guide (which is just syntactic sugar).
All of the alternative plans also work; a value pointer, surrendering copy and using unique ptr, a cow pointer, etc.
You can also roll your own any or variant, or find a stand-alone one, if you dislike boost.
template<class T, class=void>
struct has_clone_method:std::false_type{};
template<class T>
struct has_clone_method<T,
decltype( void(&T::clone) )
>:std::true_type{};
template<class T,
typename std::enable_if<!has_clone_method<T>{}, bool>::type = true
>
std::unique_ptr<T> do_clone( T const& t ) {
return std::make_unique<T>(t);
}
template<class T,
typename std::enable_if<has_clone_method<T>{}, bool>::type = true
>
std::unique_ptr<T> do_clone( T const& t ) {
return t.clone();
}
template<class T>
struct value_ptr:std::unique_ptr<T>
{
using base = std::unique_ptr<T>;
using base::base;
using base::operator=;
value_ptr()=default;
value_ptr(value_ptr&&)=default;
value_ptr& operator=(value_ptr&&)=default;
template<class D,
typename std::enable_if<std::is_base_of<T, D>::value, bool> = true
>
value_ptr( value_ptr<D> const& o ):
base( o?do_clone(*o):nullptr)
{}
template<class D,
typename std::enable_if<std::is_base_of<T, D>::value, bool> = true
>
value_ptr( value_ptr<D>&& o ):
base( std::move(o) )
{}
value_ptr( base b ):base(std::move(b)) {}
value_ptr(value_ptr const& o):
base( o?do_clone(*o):nullptr )
{}
value_ptr& operator=(value_ptr const& o) {
if (!o)
{
this->reset();
}
else if (this != &o) // test only needed for optimization
{
auto tmp = do_clone(*o);
swap( (base&)*this, tmp );
}
return *this;
}
};
template<class T, class...Args>
value_ptr<T> make_value_ptr( Args&&...args ) {
std::unique_ptr<T> retval( new T(std::forward<Args>(args)...) );
return std::move(retval);
}
class BasicJsonType {
public:
virtual std::string toString() const = 0;
virtual void setNull() = 0;
virtual bool isNull() const = 0;
virtual std::unique_ptr<BasicJsonType> clone() const = 0;
virtual ~BasicJsonType() = default;
};
using Json = value_ptr<BasicJsonType>;
using JsonVector = std::vector<Json>;
// your own to_json_string for the type in question
template<class T>
std::string to_json_string( T const& ) = delete;
std::string to_json_string( std::string const& s ) { return s; }
std::string to_json_string( double const& d )
{
std::stringstream ss;
ss << d;
return ss.str();
}
template <typename T>
class JsonValue:public BasicJsonType {
public:
JsonValue() = default;
JsonValue(JsonValue const&) = default;
JsonValue(JsonValue &&) = default;
JsonValue& operator=(JsonValue const&) = default;
JsonValue& operator=(JsonValue &&) = default;
JsonValue( T t ):value(make_value_ptr<T>(std::move(t))) {}
value_ptr<T> value;
std::string toString() const final {
if (value)
return to_json_string(*value);
else
return "(null)";
}
bool isNull() const final {
return !static_cast<bool>(value);
}
void setNull() final {
value = nullptr;
}
std::unique_ptr<BasicJsonType> clone() const final {
return std::unique_ptr<JsonValue>(new JsonValue(*this));
}
};
using JsonNumber = JsonValue<double>;
using JsonString = JsonValue<std::string>;
using JsonArray = JsonValue<JsonVector>;
std::string to_json_string( JsonVector const& arr )
{
std::stringstream ss;
ss << "{";
for (auto&& elem:arr)
{
if (elem)
{
ss << elem->toString();
}
ss << ",";
}
ss << "}";
return ss.str();
}
int main() {
JsonArray arr;
arr.value = make_value_ptr<JsonVector>();
arr.value->push_back( make_value_ptr<JsonNumber>( 3.14 ));
arr.value->push_back( make_value_ptr<JsonString>( "Hello World" ));
std::cout << arr.toString() << "\n";
}
here we make value_ptr, a smart pointer that supports copy.
It uses do_clone, which calls .clone() if it exists, and if it does not invokes their copy constructor. This permits you to make a value_ptr<T> where T is a value type, or a value_ptr<T> where T is an abstract type with a .clone() method.
I use it for a low-quality "optional" within JsonValue itself (a nullable type).
A JsonVector is then a vector of value_ptr<BasicJsonType>.
A BasicJsonType is implemented in JsonValue, where it stores it data in turn in a value_ptr<T>.
An iterative improvement would be to move the polymorphism to an internal detail.
Have a JsonValue that stores a value_ptr to a JsonBase. The JsonStorage<T> class implements JsonBase, and is not itself nullable.
JsonValue knows all 4 types that it can be. It provides interfaces that try-to-get the value of a specific type, and fail if you ask for the wrong type.
This reduces indirection, and gives the result that there isn't a NULL of type double, string, array that is distinct.
class JsonData {
public:
virtual std::string toString() const = 0;
virtual std::unique_ptr<JsonData> clone() const = 0;
virtual ~JsonData() = default;
};
using JsonPoly = value_ptr<JsonData>;
template<class T>
class JsonStorage:public JsonData {
public:
T value;
std::string toString() const final {
return to_json_string(value);
}
JsonStorage( T t ):value(std::move(t)) {}
JsonStorage() = default;
JsonStorage( JsonStorage const& )=default;
JsonStorage( JsonStorage && )=default;
JsonStorage& operator=( JsonStorage const& )=default;
JsonStorage& operator=( JsonStorage && )=default;
std::unique_ptr<JsonData> clone() const final {
return std::unique_ptr<JsonStorage>(new JsonStorage(*this));
}
};
struct JsonValue {
JsonValue() = default;
JsonValue( JsonValue const& ) = default;
JsonValue( JsonValue && ) = default;
JsonValue& operator=( JsonValue const& ) = default;
JsonValue& operator=( JsonValue && ) = default;
explicit operator bool() { return static_cast<bool>(data); }
std::string toString() const {
if (!data)
return "(null)";
else
return data->toString();
}
template<class T>
T* get() {
if (!data) return nullptr;
JsonStorage<T>* pValue = dynamic_cast<JsonStorage<T>*>(data.get());
if (!pValue) return nullptr;
return &(pValue->value);
}
template<class T>
T const* get() const {
if (!data) return nullptr;
JsonStorage<T> const* pValue = dynamic_cast<JsonStorage<T>*>(data.get());
if (!pValue) return nullptr;
return &(pValue->value);
}
JsonValue( double d ):
data( make_value_ptr<JsonStorage<double>>(d))
{}
JsonValue( std::string s ):
data( make_value_ptr<JsonStorage<std::string>>(s))
{}
JsonValue( char const* str ):
data( make_value_ptr<JsonStorage<std::string>>(str))
{}
JsonValue( std::initializer_list<JsonValue> );
private:
value_ptr<JsonData> data;
};
using JsonVector = std::vector<JsonValue>;
std::string to_json_string( JsonVector const& arr )
{
std::stringstream ss;
ss << "{";
for (auto&& elem:arr)
{
ss << elem.toString();
ss << ",";
}
ss << "}";
return ss.str();
}
JsonValue::JsonValue( std::initializer_list<JsonValue> il ):
data( make_value_ptr<JsonStorage<JsonVector>>( il ))
{}
int main() {
JsonValue arr = {JsonValue{3.14}, JsonValue{"Hello World"}};
std::cout << arr.toString() << "\n";
}
Live example.
Here, given a JsonValue v (not a template), you can ask v.get<double>() which returns a pointer-to-double if and only if the value contains a double.
JsonValue v = 3.14 works, as does JsonValue str = "hello".
Adding new types requires a to_json_string overload, and that the type supported be regular.
JsonValue is a polymorphic value type. The virtual stuff is all implementation details, not exposed to the end user. We do type erasure internally. It is basically a std::any, with an extra toString method.
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
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);
}
I have this structure of classes.
class Interface {
// ...
};
class Foo : public Interface {
// ...
};
template <class T>
class Container {
// ...
};
And I have this constructor of some other class Bar.
Bar(const Container<Interface> & bar){
// ...
}
When I call the constructor this way I get a "no matching function" error.
Container<Foo> container ();
Bar * temp = new Bar(container);
What is wrong? Are templates not polymorphic?
I think the exact terminology for what you need is "template covariance", meaning that if B inherits from A, then somehow T<B> inherits from T<A>. This is not the case in C++, nor it is with Java and C# generics*.
There is a good reason to avoid template covariance: this will simply remove all type safety in the template class. Let me explain with the following example:
//Assume the following class hierarchy
class Fruit {...};
class Apple : public Fruit {...};
class Orange : public Fruit {...};
//Now I will use these types to instantiate a class template, namely std::vector
int main()
{
std::vector<Apple> apple_vec;
apple_vec.push_back(Apple()); //no problem here
//If templates were covariant, the following would be legal
std::vector<Fruit> & fruit_vec = apple_vec;
//push_back would expect a Fruit, so I could pass it an Orange
fruit_vec.push_back(Orange());
//Oh no! I just added an orange in my apple basket!
}
Consequently, you should consider T<A> and T<B> as completely unrelated types, regardless of the relation between A and B.
So how could you solve the issue you're facing? In Java and C#, you could use respectively bounded wildcards and constraints:
//Java code
Bar(Container<? extends Interface) {...}
//C# code
Bar<T>(Container<T> container) where T : Interface {...}
The next C++ Standard (known as C++1x (formerly C++0x)) initially contained an even more powerful mechanism named Concepts, that would have let developers enforce syntaxic and/or semantic requirements on template parameters, but was unfortunately postponed to a later date. However, Boost has a Concept Check library that may interest you.
Nevertheless, concepts might be a little overkill for the problem you encounter, an using a simple static assert as proposed by #gf is probably the best solution.
* Update: Since .Net Framework 4, it is possible to mark generic parameters has being covariant or contravariant.
There are two problems here: default constructions have the form MyClass c;; with parentheses it looks like a function declaration to the compiler.
The other problem is that Container<Interface> is simply a different type then Container<Foo> - you could do the following instead to actually get polymorphism:
Bar::Bar(const Container<Interface*>&) {}
Container<Interface*> container;
container.push_back(new Foo);
Bar* temp = new Bar(container);
Or of course you could make Bar or its constructor a template as Kornel has shown.
If you actually want some type-safe compile-time polymorphism, you could use Boost.TypeTraits is_base_of or some equivalent:
template<class T>
Bar::Bar(const Container<T>& c) {
BOOST_STATIC_ASSERT((boost::is_base_of<Interface, T>::value));
// ... will give a compile time error if T doesn't
// inherit from Interface
}
No. Imagine that the container parameter is "hardcoded" into the class it defines (and that is actually how it works). Hence the container type is Container_Foo, that is not compatible with Container_Interface.
What you might try however is this:
template<class T>
Bar(const Container<T> & bar){
...
}
Yet you loose direct type checking that way.
Actually the STL way (probably more effective and generic) would be to do
template<class InputIterator>
Bar(InputIterator begin, InputIterator end){
...
}
... but I assume you don't have iterators implemented in the container.
It is possible to create an inheritance tree for containers, reflecting the inheritance tree of the data. If you have the following data:
class Interface {
public:
virtual ~Interface()
{}
virtual void print() = 0;
};
class Number : public Interface {
public:
Number(int value) : x( value )
{}
int get() const
{ return x; }
void print()
{ std::printf( "%d\n", get() ); };
private:
int x;
};
class String : public Interface {
public:
String(const std::string & value) : x( value )
{}
const std::string &get() const
{ return x; }
void print()
{ std::printf( "%s\n", get().c_str() ); }
private:
std::string x;
};
You could also have the following containers:
class GenericContainer {
public:
GenericContainer()
{}
~GenericContainer()
{ v.clear(); }
virtual void add(Interface &obj)
{ v.push_back( &obj ); }
Interface &get(unsigned int i)
{ return *v[ i ]; }
unsigned int size() const
{ return v.size(); }
private:
std::vector<Interface *> v;
};
class NumericContainer : public GenericContainer {
public:
virtual void add(Number &obj)
{ GenericContainer::add( obj ); }
Number &get(unsigned int i)
{ return (Number &) GenericContainer::get( i ); }
};
class TextContainer : public GenericContainer {
public:
virtual void add(String &obj)
{ GenericContainer::add( obj ); }
String &get(unsigned int i)
{ return (String &) GenericContainer::get( i ); }
};
This is not the best performing code; it is just to give an idea. The only problem with this approach is that every time you add a new Data class, you have to also create a new Container. Apart from that, you have polymorphism "working again". You can be specific or general:
void print(GenericContainer & x)
{
for(unsigned int i = 0; i < x.size(); ++i) {
x.get( i ).print();
}
}
void printNumbers(NumericContainer & x)
{
for(unsigned int i = 0; i < x.size(); ++i) {
printf( "Number: " );
x.get( i ).print();
}
}
int main()
{
TextContainer strContainer;
NumericContainer numContainer;
Number n( 345 );
String s( "Hello" );
numContainer.add( n );
strContainer.add( s );
print( strContainer );
print( numContainer );
printNumbers( numContainer );
}
I propose the following workaround, which employs a template function. Although the example use Qt's QList, nothing prevents the solution from being straightforwardly transposed to any other container.
template <class D, class B> // D (Derived) inherits from B (Base)
QList<B> toBaseList(QList<D> derivedList)
{
QList<B> baseList;
for (int i = 0; i < derivedList.size(); ++i) {
baseList.append(derivedList[i]);
}
return baseList;
}
Pros:
general
type-safe
fairly efficient if the items are pointers or some other cheaply copy-constructible elements (such as implicitly shared Qt classes)
Cons:
requires the creation of a new container, as opposed to enabling the reuse of the original one
implies some memory and processor overhead both to create and to populate the new container, which depend heavily on the cost of the copy-constructor
#include <iostream>
#include <sstream>
#include <map>
#include <vector>
struct Base { int b = 111; };
struct Derived: public Base { };
struct ObjectStringizer {
template <typename T>
static std::string to_string(const T& t) {
return helper<T>()(t);
}
template <typename T, typename = void>
struct helper {
std::string operator()(const T& t) {
std::ostringstream oss;
oss << t;
return oss.str();
}
};
template <typename T>
struct helper<T, typename std::enable_if<std::is_base_of<Base, T>::value>::type> {
std::string operator()(const T& base) {
return to_string(base.b);
}
};
template <typename T>
struct helper<std::vector<T>> {
std::string operator()(const std::vector<T>& v) {
std::ostringstream oss;
for (size_t i = 0, sz = v.size(); i < sz; ++i) {
oss << (i ? "," : "") << to_string(v[i]);
}
return "[" + oss.str() + "]";
}
};
template <typename Key, typename Value>
struct helper<std::map<Key, Value>> {
std::string operator()(const std::map<Key, Value>& m) {
std::ostringstream oss;
for (auto iter = m.begin(), iter_end = m.end(); iter_end != iter; ++iter) {
oss << (m.begin() != iter ? "," : "") << to_string(iter->first) << ":" << to_string(iter->second);
}
return "{" + oss.str() + "}";
}
};
};
int main(int argc, char* argv[]) {
std::cout << ObjectStringizer::to_string("hello ") << ObjectStringizer::to_string(std::string("world")) << std::endl;
std::cout << ObjectStringizer::to_string(Derived()) << std::endl;
std::cout << ObjectStringizer::to_string(std::vector<int>{3, 5, 7, 9}) << std::endl;
std::cout << ObjectStringizer::to_string(std::map<int, std::string>{{1, "one"}, {2, "two"}}) << std::endl;
return 0;
}
container is a container of Foo objects not a container of Interface objects
And it cannot be polymorphic either, pointers to things can be ,but not the objects themselvs. How big would the slots in the container have to be for container if you could put anything derived from interface in it
you need
container<Interface*>
or better
container<shared_ptr<Interface> >