extensible library interface without reinterpret_cast - c++

I am building a static library that will be used on many future projects. I do not want to limit the interface of a particular function in this static library so the application codes can have flexibility in data types. This library will hold pure virtual base class pointers to objects the user will need.
At first, I tried templating this function. However, I would have to template the pure virtual base function as well (In the code shown, BBInterface::Go) - apparently this is not possible.
The visitor pattern sounded like it might be applicable, but I'm afraid I just don't get it. I furthermore don't understand if I can keep the static library black-boxed, or if the static library would have to be re-compiled and linked with a new set of "visitors" anytime someone adds a possible data type.
I am now trying to create a templated struct inside the function, which is then reinterpret_cast-ed to a (hopefully?) equivalent struct. See below
This seems to work for two inputs (A and B). This is ok, but I'd ideally want to use variadic templates to have potentially many inputs. This is over my head at this point. If anyone could help with that, it'd be great.
So, is there a more elegant way to keep an extensible function interface (BBContainer::Do in the code below)? Is there a way to avoid reinterpret_cast? Can I extend this to more than two templated arguments? Is there a way to check for success of reinterpret_cast like dynamic_cast?
#include <iostream>
#include <vector>
#include <map>
#include <memory>
using namespace std;
static const double values[] = {0., 1., 2., 3., 4., 5., 6. };
// ------ ASSUME THIS BLACK BOX AREA IS IN A STATIC LIBRARY THE USER CAN NOT MODIFY -------//
struct BBPacket {};
class BBInterface
{
public:
virtual void Go(BBPacket&) = 0;
};
class BBContainer
{
public:
void Add(const string aName, std::unique_ptr<BBInterface>&& aThing)
{
BBMap[aName] = std::move(aThing);
}
template <typename A, typename B>
void Do(const std::string& aName, A& aVal, const B& aIndex)
{
struct NewPacket : public BBPacket
{
NewPacket(A& aVal, const B& aIndex) : mVal(aVal), mIndex(aIndex) {}
A& mVal;
const B& mIndex;
};
NewPacket temp(aVal, aIndex);
this->Do(aName, temp);
}
void Do(const string& aName, BBPacket& aPacket)
{
BBMap[aName]->Go(aPacket);
}
private:
map<std::string, unique_ptr<BBInterface>> BBMap;
};
// ----- The user area is written by the user, and should not be included in the blackbox project! ---------
struct USingleValuePacket
{
double& mVal;
const int& mIndex;
};
struct UVectorValuePacket
{
vector<double>& mVals;
const vector<int>& mIndices;
};
class USingleExtractor : public BBInterface
{
virtual void Go(BBPacket& aPacket)
{
USingleValuePacket& danger = reinterpret_cast<USingleValuePacket&>(aPacket);
fprintf(stdout, "The current single value is %1.1f\n", danger.mVal);
danger.mVal = values[danger.mIndex];
}
};
class UVectorExtractor : public BBInterface
{
virtual void Go(BBPacket& aPacket)
{
UVectorValuePacket& danger = reinterpret_cast<UVectorValuePacket&>(aPacket);
for (int i = 0; i < danger.mVals.size(); ++i)
{
fprintf(stdout, "The current vector value %i is %1.1f\n",i, danger.mVals[i]);
danger.mVals[i] = values[danger.mIndices[i]];
}
}
};
int main()
{
BBContainer a;
a.Add("f", std::unique_ptr<USingleExtractor>(new USingleExtractor));
a.Add("g", std::unique_ptr<UVectorExtractor>(new UVectorExtractor));
double val = 0.;
int index = 4;
a.Do("f", val, index);
fprintf(stdout, "Expected value is 4.0 and I get %1.1f\n", val);
std::vector<double> valVec(3);
std::vector<int> indexVec; indexVec.push_back(0); indexVec.push_back(2); indexVec.push_back(5);
a.Do("g", valVec, indexVec);
fprintf(stdout, "Expected value for index 0 is 0.0 and I get %1.1f\n", valVec[0]);
fprintf(stdout, "Expected value for index 1 is 2.0 and I get %1.1f\n", valVec[1]);
fprintf(stdout, "Expected value for index 2 is 5.0 and I get %1.1f\n", valVec[2]);
// a.Do("g", val, index); // This will go into UVectorExtractor with USingleValuePacket data - Not compatible!
return 0;
}
EDIT:
I want BBContainer::Do to have a flexible signature (in this example I use (string, double&, const int&) and (string, vector&, const vector&), but I may have many more). At the same time I do not want to modify BBInterface (for example with Go(double&, const int), Go(vector&, const vector&), and so on). I can assume the derived class of BBInterface knows what data its particular implementation of Go requires. So how do I forward generic data from BBContainer::Do to the derived classes of BBInterface when it only has access to the BBInterface base class - which is not allowed to be specialized? And, is there a more type-safe method than generating a templated struct in the BBInterface base class and using reinterpret_cast in its derived classes?

As Hurkyl pointed out, I should just make a packet. It seems good enough with that and a helper function to keep the interface of Do clean and a dynamic_cast instead of reinterpret_cast. I'm still working on variadic templates for variable length packets.
New packet:
template<typename A, typename B>
struct UPacket : public BBPacket
{
UPacket(A& aVal, const B& aIndex) : mVal(aVal), mIndex(aIndex) {}
A& mVal;
const B& mIndex;
};
Helper function:
template <typename A, typename B>
void Do(BBContainer& a, const string& aName, A& aVal, const B& aIndex)
{
a.Do(aName, UPacket<A, B>(aVal, aIndex));
}
Usage:
...
double val = 0.;
int index = 4;
Do(a,"f", val, index);
...
std::vector<double> valVec(3);
std::vector<int> indexVec; indexVec.push_back(0); indexVec.push_back(2); indexVec.push_back(5);
Do(a, "g", valVec, indexVec);

Related

unique container for template class

I have an algorithm (not preseted here) which takes as input different parameters (int, float, vectors).
My idea of design was to have an container which holds all these differents parameters.
To achive this, I have a base class Parameter and a derivated template class TypeParameter.
These parameters will be holded in a container.
The design is presented below:
#pragma once
#include <utility>
#include <memory>
#include <string>
#include <vector>
namespace parameter
{
/*
Interface for parameter
*/
class Parameter
{
public:
Parameter() {}
Parameter(std::string param_name) : name(param_name) {}
Parameter(const Parameter&& other) noexcept : name(std::move(other.name)) {}
virtual ~Parameter() {}
inline const std::string get_name() { return name; }
private:
std::string name;
};
/*
*/
template<class T>
class TypeParameter
: public Parameter
{
public:
TypeParameter(std::string param_name, T new_value) : Parameter(param_name), value(new_value) {}
TypeParameter(const TypeParameter&& other) noexcept : Parameter(std::move(other)), value(std::move(other.T)) {}
inline const T get_value() { return value; }
private:
T value;
};
/*
Container for parameters
*/
class ParameterSet
{
public:
ParameterSet() {}
void add(std::unique_ptr<Parameter> param) { data.push_back(std::move(param)); }
private:
std::vector <std::unique_ptr<Parameter>> data;
};
} //namespace parameter
The main is:
#include <iostream>
#include <string>
#include "Parameter.h"
using parameter::TypeParameter;
using parameter::Parameter;
using parameter::ParameterSet;
void foo(std::unique_ptr<Parameter> p)
{
std::cout << p->get_value(); // ERROR
}
int main(int argc, char *argv[])
{
TypeParameter<int> *iparam = new TypeParameter<int>("ee", 3);
std::unique_ptr<Parameter> p = std::make_unique <TypeParameter<int>>("foo", 3);
foo(std::move(p));
ParameterSet param_set;
param_set.add(std::unique_ptr<Parameter>(iparam));
param_set.add(std::move(p));
getchar();
}
My problem is I cannot get the value without a cast.
Hence, my question is how do I cast the unique_ptr from a Parameter class to derived TypeParameter.
Is there another way to design the container?
Thanks a lot!
You don't have to reinvent the wheel. There are a couple of classes you can use from the standard library:
std::variant.
As suggested by the comments, variant is a type-safe union of a pre-defined set of data types, which you put in the templates argument of variant.
For example, a std::variant<int,float,double> can hold any value of type int, float, or double, but nothing else.
To use the stored value, you can either use the visitor pattern with the std::visit() function. Other functions allow you to know which of the preset types is stored in the variable (index()) and to extract the value from it (using get()). If you try to extract the value of the wrong type, the get() function throws an exception
std::any
is another utility that can hold different data types. As opposed to variant, you don't have to know the types at compile-time. Basically, it stores a void* to the data with a typeinfo to remember its original type. You can then use any_cast to cast the variable back to its original type. Just like variant, an exception is thrown when trying to cast to the wrong type.
These two classes are available in C++ 17. If these features are not available to you, they were also included in boost (respectively boost:variant and boost:any)
You can store the set of values in a standard library container, e.g. in a std::vector<std::variant<int,float,double>> or a std::vector<std::any>>.
Alternative to std::variant/std::any is the old way polymorphism:
class Parameter
{
public:
Parameter(const std::string& param_name) : name(param_name) {}
virtual ~Parameter() = default;
const std::string& get_name() const { return name; }
virtual void printValue() const = 0;
// Other virtual methods
private:
std::string name;
};
template<class T>
class TypeParameter : public Parameter
{
public:
TypeParameter(const std::string& name, const T& t) : Parameter(name), value(t) {}
// Non virtual method when we don't access it by base class.
const T& get_value() const { return value; }
void printValue() const { std::cout << value; }
private:
T value;
};
And then your
void foo(const Parameter& p)
{
std::cout << p.get_value(); // ERROR
}
becomes
void foo(const Parameter& p)
{
p.print();
}
If you don't want to add many virtual methods to Parameter, then Visitor pattern can help, but then you have to know each derived types.

Combining typesafe code with runtime decisions

I am in the process of rewriting some existing code - where previously, all answer information was stored in a string array in memory. Based on the datatype, the data was transformed in various places. Below is a quick mock up of the setup I am aiming for. Essentially you have some questions - and the structure of the answers stored in the database depends on the datatype. Generally I avoid dealing with void*, and casting them to an appropriate type - but I couldn't find a better solution that would allow me to run generic code (by means of lambdas), or be specific if the datatype is known. Templated classes won't help in this case, as all the answers need to be stored in the same vector (as some arithmetic are applied to all answers based on predefined rules).
Any advice is appreciated.
#include <vector>
#include <memory>
struct AddressData
{
wchar_t Line1[50];
wchar_t Line2[50];
long CountrySeqNo;
AddressData()
{
memset(this, 0, sizeof(*this));
};
};
struct GenericData
{
wchar_t value[200];
GenericData()
{
memset(this, 0, sizeof(*this));
};
};
enum class DataType
: short
{
GENERIC,
ADDRESS
};
class AnswerBase
{
protected:
const void* const data;
const DataType dataType;
protected:
AnswerBase(const DataType datatype, const void* const _data)
: dataType(datatype), data(data)
{
if (data == nullptr)
throw std::exception("Data may not be initialized as NULL");
};
public:
/*
Some generic methods here that would apply logic by means of lambdas etc - these would be overwritten in the derived classes
*/
template<typename T> const T& GetData() { static_assert(false, "The given type is not supported"); };
template<>
const GenericData& GetData()
{
if (DataType::GENERIC != dataType)
throw std::exception("The requested type does not match the value that initialised data");
return *static_cast<const GenericData* const>(data);
};
template<>
const AddressData& GetData()
{
if (DataType::ADDRESS != dataType)
throw std::exception("The requested type does not match the value that initialised data");
return *static_cast<const AddressData* const>(data);
};
};
class AddressAnswer
: public AnswerBase
{
public:
AddressAnswer()
: AnswerBase(DataType::ADDRESS, &answer)
{
};
protected:
AddressData answer;
};
class GenericAnswer
: public AnswerBase
{
public:
GenericAnswer()
: AnswerBase(DataType::GENERIC, &answer)
{
};
protected:
GenericData answer;
};
int main()
{
std::vector<std::shared_ptr<AnswerBase>> answers;
answers.push_back(std::make_shared<GenericAnswer>());
answers.push_back(std::make_shared<AddressAnswer>());
// In some parts of code - interact with generic methods without needing to check the underlying data type
// ....
// ....
// In parts of code where we know we are dealing with a given type - like saving to a DB
auto val1 = answers[0]->GetData<GenericData>().value;
auto val2 = answers[1]->GetData<AddressData>().Line1;
// this will give a runtime failure
//auto val3 = answers[0]->GetData<AddressData>().Line1;
return 0;
}
variant is the clean way to do this. Store it in the parent.
Alternatively, provide a variant<A,B> GetData() in the parent. Now visiting is encapsulated in the variant returned. The parent stores the data.
Alternatively, provide a virtual variant<A,B> GetData() = 0. The child type returns the data, either A or B, in the variant in question.
Alternatively, write virtual A* GetA() = 0; virtual B* GetB() = 0;. Then maybe write a template method called GetData<T> such that GetData<A>() calls GetA, etc.
Alternatively, write virtual A* Get(tag_t<A>) = 0; virtual B* Get(tag_t<B>)=0;, where
template<class T>
struct tag_t {
using type=T;
constexpr tag_t(){}
};
template<class T>
constexpr tag_t<T> tag{};
is a tag used for dispatching. Now you can call the right virtual interface by doing a Get(tag<AddressData>).
In these virtual cases, the data is stored in the derived type.

Initializing a vector with class template of different / unknown types

Working on a command line parser for myself. I knew immediately that I was going to have trouble with this construct and hoping someone could provide suggestions for a work around.
I want to store the argument list of parameters (based off a template) in a vector that will possibly contain a variety of different data types. But from my understanding, you have to define the vector<template<type>> statically. Is there a way to except multiple types?
Here is an example of what I mean:
#include <vector>
#include <memory>
namespace clparser
{
class CommandLine {
private:
std::vector<Parameter<AnyType??>*> ArgumentList;
public:
void Add(Parameter<AnyType??>* Parameter) { ArgumentList.push_back(Parameter); }
};
template<typename T>
class Parameter {
private:
const char *ShortOption;
const char *LongOption;
const char *Description;
const bool RequiredField;
const char *DefaultValue;
public:
Parameter(const char *ShortOption, const char *LongOption, const char *Description, const bool RequiredField, const char *DefaultValue)
: ShortOption(ShortOption), LongOption(LongOption), Description(Description), RequiredField(RequiredField), DefaultValue(DefaultValue)
{
}
};
}
If you can accept a C++11 solution, I propose you a iper-simplified version from my command line parser. Hoping that can be of inspiration for you.
The idea behind my solution is the use of base/derived polymorphism: a pure virtual class optBase that is a base for a set of template classes dedicated to options (in the following example, only class opt; but there are other three in my parser).
Then the (not template) class yData contain a std::unique_ptr<optBase> (if you use a simple pointer to optBase you can compile in C++98 too, I suppose; but I suggest the use of C++11 or newer).
class yData correspond (roughly) to your tou your class Parameter but (here is the trick) isn't a template class; contain a base pointer to a template class.
My class yarg correspond to your class clparser and my std::map<int, yData> idMap correspond (roughly) to your std::vector<Parameter<AnyType??>*> ArgumentList.
To feed idMap, I've developed a set of template method (one for every derived from optbase classes); in the following example you can see a iper-semplified version of one of them: addOpt() (corresponding, roughly, to your Add()).
In the following example you can see a little main() with a couple of uses for addOpt(): the first for a int parameter and the second for a double parameter (important (and weak point of my solution): the returned value must be saved in a reference variable, not in a simple variable).
#include <map>
#include <memory>
class optBase
{
public:
// some pure virtual methods
};
template <typename X>
class opt : public optBase
{
private:
X val { };
// ...
public:
opt ()
{ }
opt (X const & v0)
: val { v0 } // ...
{ }
X const & getVal () const
{ return val; }
X & getVal ()
{ return val; }
// ...
};
// other optBase derived classes (for flags, containers of values, etc)
class yData
{
private:
// ...
std::unique_ptr<optBase> optB;
public:
yData (/* other arguments */ std::unique_ptr<optBase> optB0)
: /* something else */ optB { std::move(optB0) }
{ }
// ...
std::unique_ptr<optBase> const & getPnt () const
{ return optB; }
};
class yarg
{
private:
// ...
std::map<int, yData> idMap;
// ...
public:
// ...
template <typename T>
T & addOpt (/* other arguments */ T const & def = T())
{
int id { /* some value */ };
opt<T> * optP { nullptr };
// ...&
idMap.emplace(std::piecewise_construct,
std::forward_as_tuple(id),
std::forward_as_tuple(/* other arguments */
std::unique_ptr<optBase>(optP = new opt<T>(def))));
return optP->getVal();
}
};
int main ()
{
yarg y;
// important: use a *reference*
auto & pi = y.addOpt(3); // pi is a int
auto & pd = y.addOpt(3.0); // pd is a double
static_assert(std::is_same<decltype(pi), int &>::value, "!");
static_assert(std::is_same<decltype(pd), double &>::value, "!!");
}

How to implement generically typed member objects in C++?

I have an application which creates simple music visualization animations. These animations are driven by nodes, and each node has a bunch of parameters that could have one of several types: int, float, color, etc. The parameters can either have a user-set value, or can be connected to the output of another node.
I'm currently using a templated type, along with std::function<>, like this:
#include <functional>
template <class PT>
class Param
{
public:
Param(PT value=PT()) : _value(value), _provider(nullptr) {}
void setValue(const PT &value) {_value = value;}
void setProvider(std::function<void(PT&)> provider) {_provider = provider;}
void getCurrentValue(PT &value) {
// update current member value
if (_provider)
_provider(_value);
value = _value;
}
private:
PT _value;
std::function<void(PT &value)> _provider;
};
I then instantiate parameters for an animated nodes like this:
class AnimationNode
{
public:
AnimationNode(Model *model = nullptr);
void evaluate();
private:
Param<int> _xoffset;
Param<int> _yoffset;
Param<float> _scale;
Param<ColorType> _color;
};
These parameters could be connected to a driver node, such as this one:
class SublevelMeter {
public:
SublevelMeter();
void setRange(Subrange &_range);
...
std::function<void(float&)> createProviderClosure();
private:
float _level;
...
}
std::function<void(float&)> SublevelMeter::createProviderClosure() {
return [this] (float &out) {out = _level;};
}
And connect one node to another by doing something like this:
AnimationNode::connectScaleToSublevel(SublevelMeter *slm) {
_scale->setProvider(slm->createProviderClosure());
}
The problem is, I'd like there to be an abstract Param type that I can pass to objects, so rather than the code above, I could pass a param to my SublevelMeter:
SublevelMeter::connectToParam(Param *param) {
param->setProvider(slm->createProviderClosure());
}
This would also help when writing the routines that create my GUI editor widgets: the editor could figure out the correct type by introspection of the Param.
But I'm not sure how to do this from a templated class, nor how the best way to implement the introspection in C++. (I'm coming at this from a python design background, which is perhaps encouraging me to think about this in a pythonic rather than C++ way; if there's a better way to approach this, I'd love to hear about it!)
I'm using Qt, so I've considered using QVariant, or other Qt Meta-Object stuff, but I'm not sure how to make that work, or if it would even be appropriate. (I'm not using Boost, and while I know it has certain type erasure facilities, I'm wary of wading into those waters...)
I'm interested in what the cleanest/"best" way to do this. Although efficiency is a consideration (getCurrentValue() is called many times per frame while the animation is playing) I can still probably afford run-time overhead of dynamic type stuff.
At least the first part of your question is solvable without abstract Param:
class SublevelMeter {
...
template<class PT>
void connectToParam(Param<PT> *param) {
param->setProvider(createProviderClosure<PT>());
}
// specialize this for different PTs
template<class PT>
std::function<void(PT&)> createProviderClosure();
}
If you really need to manipulate dynamic lists of Param-s, and you don't want to use any kind of RTTI, consider using Visitor pattern:
class Visitor;
class ParamBase
{
public:
virtual ~ParamBase() = default;
virtual void acceptVisitor(Visitor* v) = 0;
};
template <class PT>
class Param : public ParamBase
{
public:
...
void acceptVisitor(Visitor* v) override;
};
class Visitor {
public:
virtual ~Visitor() = default;
void visit(ParamBase* p) {
p->acceptVisitor(this);
}
virtual void visitParam(Param<float>* p) = 0;
// add more functions for other Params
};
class PrintVisitor : public Visitor {
public:
void visitParam(Param<float>* p) override {
std::cout << "visited Param<float>, value = " << p->getValue() << std::endl;
}
};
template<class PT>
void Param<PT>::acceptVisitor(Visitor* v) {
v->visitParam(this);
}
int main() {
std::unique_ptr<ParamBase> p(new Param<float>(123.4f));
std::unique_ptr<Visitor> v(new PrintVisitor());
v->visit(p.get());
return 0;
}
I implemented for you a simple class for the generic type management. This class is implemented without using template, so you can declare your variables and assign a value and a type directly at runtime. This implementation is very simple you should use it as reference to develop your own solution. In the following example I implemented the support for only 3 types: int, double and char* (C string). The main function shows you as to use the generic type class for both LVALUE and RVALUE assignment:
#include <stdio.h>
#include <stdlib.h>
enum Types {tInteger, tDouble, tString};
class TGenericType
{
private:
char m_Value[100];
Types m_Type;
protected:
public:
void operator=(int AValue)
{
m_Type = tInteger;
sprintf(m_Value, "%d", AValue);
}
operator int()
{
// try to convert the m_Value in integer
return atoi(m_Value); // the result depend by atoi() function
}
void operator=(double AValue)
{
m_Type = tDouble;
sprintf(m_Value, "%f", AValue);
}
operator double()
{
// try to convert the m_Value in double
return atof(m_Value); // the result depends by atof() function
}
void operator=(char* AValue)
{
m_Type = tString;
strcpy(m_Value, AValue);
}
operator char*()
{
return m_Value;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
TGenericType LVar;
// int assignment LVar used as LVALUE
LVar = 10;
// int assignment LVar used as RVALUE
int i = LVar;
// Double assignment LVar used as LValue
LVar = 10.1;
// double assignment LVar used as RVALUE
double d = LVar;
// costant string assignment LVar used as RVALUE
LVar = "Ciao Mondo";
// string copying LVar used as const string RVALUE
char Buffer[100];
strcpy(Buffer, LVar);
return 0;
}
I tested above code on c++builder 32bit and c++builder (CLang) 64bit
If my solution answer your question, please check it as answered.
Ciao from Italy!
Angelo

C++ Push Multiple Types onto Vector

Note: I know similar questions to this have been asked on SO before, but I did not find them helpful or very clear.
Second note: For the scope of this project/assignment, I'm trying to avoid third party libraries, such as Boost.
I am trying to see if there is a way I can have a single vector hold multiple types, in each of its indices. For example, say I have the following code sample:
vector<something magical to hold various types> vec;
int x = 3;
string hi = "Hello World";
MyStruct s = {3, "Hi", 4.01};
vec.push_back(x);
vec.push_back(hi);
vec.push_back(s);
I've heard vector<void*> could work, but then it gets tricky with memory allocation and then there is always the possibility that certain portions in nearby memory could be unintentionally overridden if a value inserted into a certain index is larger than expected.
In my actual application, I know what possible types may be inserted into a vector, but these types do not all derive from the same super class, and there is no guarantee that all of these types will be pushed onto the vector or in what order.
Is there a way that I can safely accomplish the objective I demonstrated in my code sample?
Thank you for your time.
The objects hold by the std::vector<T> need to be of a homogenous type. If you need to put objects of different type into one vector you need somehow erase their type and make them all look similar. You could use the moral equivalent of boost::any or boost::variant<...>. The idea of boost::any is to encapsulate a type hierarchy, storing a pointer to the base but pointing to a templatized derived. A very rough and incomplete outline looks something like this:
#include <algorithm>
#include <iostream>
class any
{
private:
struct base {
virtual ~base() {}
virtual base* clone() const = 0;
};
template <typename T>
struct data: base {
data(T const& value): value_(value) {}
base* clone() const { return new data<T>(*this); }
T value_;
};
base* ptr_;
public:
template <typename T> any(T const& value): ptr_(new data<T>(value)) {}
any(any const& other): ptr_(other.ptr_->clone()) {}
any& operator= (any const& other) {
any(other).swap(*this);
return *this;
}
~any() { delete this->ptr_; }
void swap(any& other) { std::swap(this->ptr_, other.ptr_); }
template <typename T>
T& get() {
return dynamic_cast<data<T>&>(*this->ptr_).value_;
}
};
int main()
{
any a0(17);
any a1(3.14);
try { a0.get<double>(); } catch (...) {}
a0 = a1;
std::cout << a0.get<double>() << "\n";
}
As suggested you can use various forms of unions, variants, etc. Depending on what you want to do with your stored objects, external polymorphism could do exactly what you want, if you can define all necessary operations in a base class interface.
Here's an example if all we want to do is print the objects to the console:
#include <iostream>
#include <string>
#include <vector>
#include <memory>
class any_type
{
public:
virtual ~any_type() {}
virtual void print() = 0;
};
template <class T>
class concrete_type : public any_type
{
public:
concrete_type(const T& value) : value_(value)
{}
virtual void print()
{
std::cout << value_ << '\n';
}
private:
T value_;
};
int main()
{
std::vector<std::unique_ptr<any_type>> v(2);
v[0].reset(new concrete_type<int>(99));
v[1].reset(new concrete_type<std::string>("Bottles of Beer"));
for(size_t x = 0; x < 2; ++x)
{
v[x]->print();
}
return 0;
}
In order to do that, you'll definitely need a wrapper class to somehow conceal the type information of your objects from the vector.
It's probably also good to have this class throw an exception when you try to get Type-A back when you have previously stored a Type-B into it.
Here is part of the Holder class from one of my projects. You can probably start from here.
Note: due to the use of unrestricted unions, this only works in C++11. More information about this can be found here: What are Unrestricted Unions proposed in C++11?
class Holder {
public:
enum Type {
BOOL,
INT,
STRING,
// Other types you want to store into vector.
};
template<typename T>
Holder (Type type, T val);
~Holder () {
// You want to properly destroy
// union members below that have non-trivial constructors
}
operator bool () const {
if (type_ != BOOL) {
throw SomeException();
}
return impl_.bool_;
}
// Do the same for other operators
// Or maybe use templates?
private:
union Impl {
bool bool_;
int int_;
string string_;
Impl() { new(&string_) string; }
} impl_;
Type type_;
// Other stuff.
};