Reputation: 691
I have created a dynamically expanding container class using template. But this can only store objects of same datatype. How can i create a container class that expands dynamically and can hold objects of types specified when expanding the class.
That is for eg:-
if obj is an object of said class I should be able to call a function say
obj.foo<int>(25);
and after that the same function to store second member as a Boolean type or float
obj.foo<bool>(true);
is there any way to do this I have thought about templates but I cannot set a class member without knowing the datatype before hand
Upvotes: 1
Views: 711
Reputation: 569
here's a c++03 way:
#include <vector>
#include <string>
#include <typeinfo>
class Entry{
public:
class Exception_TypeFail : std::exception {
public:
~Exception_TypeFail() throw() {
}
const char* what() const throw(){
return "Failed to cast";
}
};
class PolymorphInterface{
public:
virtual ~PolymorphInterface() {};
virtual bool isType(const std::type_info &testType) const =0;
};
template<typename TYPE>
class Polymorph : public PolymorphInterface{
private:
TYPE mData;
public:
Polymorph(const TYPE ©Me)
: mData(copyMe) {
}
bool isType(const std::type_info &testType) const {
return testType == typeid(mData);
}
TYPE& get(){
return mData;
}
};
Entry(PolymorphInterface *manageMe)
: mManageMe(manageMe) {
}
Entry(const Entry ©Me)
: mManageMe(const_cast<Entry&>(copyMe).mManageMe) {
const_cast<Entry&>(copyMe).mManageMe = NULL;
}
~Entry(){
delete mManageMe;
}
template<typename TYPE>
bool isType(){
return mManageMe->isType(typeid(TYPE));
}
template<typename TYPE>
TYPE& get(){
if(!mManageMe->isType(typeid(TYPE)))
throw Exception_TypeFail();
return dynamic_cast< Polymorph<TYPE>* >(mManageMe)->get();
}
template<typename TYPE>
const TYPE& getConst() const {
if(!mManageMe->isType(typeid(TYPE)))
throw Exception_TypeFail();
return dynamic_cast< Polymorph<TYPE>* >(mManageMe)->get();
}
private:
PolymorphInterface *mManageMe;
};
int main(){
//- I'm using std lib containers the same way your container may work
std::vector<Entry> magicContain;
magicContain.push_back(
Entry(new Entry::Polymorph<int>(42))
);
magicContain.push_back(
Entry(new Entry::Polymorph<float>(1.5f))
);
magicContain.push_back(
Entry(new Entry::Polymorph<std::string>("hi there."))
);
if(magicContain[0].isType<long>())
long iShouldNotBeHere = 0;
std::string impressYourFriends;
if(magicContain[2].isType<std::string>())
impressYourFriends = magicContain[2].get<std::string>();
const std::vector<Entry> frozenMagic( magicContain );
return frozenMagic[0].getConst<int>();
}
Upvotes: 1
Reputation: 118300
C++17 introduces std::any
, a class that can store an arbitrary object, so you could simply use your container class to store std::any
objects.
This is really just a C++ library addition, and it does not really make use of any core C++ language features, so before C++17 one can simply implement their own version of std::any
.
This, however, is quite a bit of work. You will need to make use of advanced language features like placement new, for example. This is not something that can be explained in a few short paragraphs.
But another way it might be possible to do this without too much pain would be to take advantage of std::shared_ptr
's type erasure features, and have your container store std::shared_ptr<void>
objects. Your insertion function template could use make_shared
to construct a std::shared_ptr
to whatever type is being added to your container, then use std::static_pointer_cast<void>
to convert it to a std::shared_ptr<void>
, then store it in your container.
But then, of course, you would have to figure out the type of each object stored in your container, which can get quite painful.
And if all the different classes your container can store are all subclasses of the same base class, then you can simply shove std::shared_ptr<BaseClass>
in your container, and call it a day. That would be the more straightforward solution.
Upvotes: 2