I have this problem again and again... and still have not a satisfactory answer...
Especially when I put the class into a container, later on I need to record more information on every element in the container during a specific processing, but after processing I do not need the extra information anymore....
I often found some libraries try to solve the above situation by defining a void* in their data structure to provide user-defined data structure extension. Just the same described in this Q&A.
But it produces memory / resource handling problem... and other problems that I feel this approach is error-prone.
In the modern day of object-oriented programming, I am thinking of
using inheritance & polymorphism. Use base class's pointer in the container, but then I have to add derived class's accessor into the base class. It is kind of strange...
is there any other better ways to extend a class's property while maintain container comparability in C++?
The best way to store extra data about a object without actually compromising the integrity of the object itself is to store a pair of data in the container instead.
struct User { ... };
struct ExtraData { ... };
typedef std::pair<User, ExtraData> UserAndExtraData;
Now I can create a container type in C++ which stores both pieces of information together without compromising the independence of either type.
std::vector<UserAndExtraData> vector;
I would look into the Decorator Pattern. You can decorate your objects while processing them then throw the decorated objects away. If there is a lot of shared data you can also look into the FlyWeight pattern.
"User" could be extended by template parameters. for example,
template <typename... Extra>
struct User : Extra...
{
...
};
struct ExtraData {...};
struct ExtraExtraData {...};
using ExtraUser = User<ExtraData>;
using MoreExtraUser = User<ExtraData, ExtraExtraData>;
In the modern day of object-oriented programming, I am thinking of
using inheritance & polymorphism. Use base class's pointer in the
container, but then I have to add derived class's accessor into the
base class. It is kind of stange...
you don't need to put a pointer to your derived class in your base class when using inheritance. You just need to cast to the derived class. the problem is getting your data into the derived objects when it's stored in the base objects - you can only cast them if they were created as the derived type, even if your collection holds them as the base type. (if they are created as the derived type, then just cast!)
So if you have a collection of BaseC, you can create a new class DerivedC that has a copy constructor that takes a BaseC. You can copy your BaseC object into it, perform your processing on the DerivedC objects and then copy these back into a BaseC object for storage. This uses the Flyweight pattern. Note that if you have a collection of BaseC objects, you cannot just pretend they are DerivedC classes as they will not have the storage to hold all the data members, you need to create new DerivedC objects.
Alternatively, create a new class just for processing that contains a (smart pointer) reference to your base class objects, copy the reference in, perform the processing, delete the processing objects when you're done.
If your objects are in a vector, then a simple approach is to make a parallel vector:
void doSomething(const vector<MyObject>& my_objects)
{
vector<ExtraData> extra_data;
int n_objects = extra_data.size();
extra_data.reserve(n_objects);
for (int i=0; i!=n_objects; ++i) {
extra_data.push_back(calcExtraData(my_objects[i]));
}
// now use my_objects[i] and extra_data[i] together.
// extra data goes away when the function returns.
}
You don't have to modify your original objects, and it is very efficient.
If you have some other container type, you can use a map:
void doSomething(const set<MyObject>& my_objects)
{
map<MyObject*,ExtraData> extra_data;
set<MyObject>::const_iterator i=my_objects.begin(), end=my_objects.end();
for (;i!=end;++i) {
extra_data[&*i] = calcExtraData(*i);
}
// now use extra_data[&obj] to access the extra data for obj.
// extra data goes away when the function returns.
}
this isn't as efficient as with vectors, but you still don't have to modify your original classes.
However, it becomes more difficult to maintain the parallel structures if the underlying container can change during the processing.
One simple option is to add a type parameter representing the "extra data"...
template<class ExtraDataType>
struct MyExtensibleContainer
{
...
ExtraDataType extra;
};
Perhaps if you indicate why this solution isn't sufficient, the true requirements will come through.
Example for int and void*:
struct IntOrVoid
{
};
struct IntOrVoid1 : IntOrVoid
{
int x;
};
struct IntOrVoid2 : IntOrVoid
{
void* x;
};
typedef shared_ptr<IntOrVoid> PIntOrVoid;
then use MyExtensibleContainer<PIntOrVoid>
or altenatively:
union IntOrVoid
{
int x_int;
void* x_voidp;
};
then use MyExtensibleContainer<IntOrVoid>
The problem you are describing has nothing to do with adding an "extra" data type. The problem you are describing has to do with holding a variant type that can have one of many hetrogeneous types. There are many ways to do this, it is a much more general problem.
Related
Let's assume this class hierarchy below.
class BaseClass {
public:
int x;
}
class SubClass1 : public BaseClass {
public:
double y;
}
class SubClass2 : public BaseClass {
public:
float z;
}
...
I want to make a heterogeneous container of these classes. Since the subclasses are derived from the base class I can make something like this:
std::vector<BaseClass*> container1;
But since C++17 I can also use std::variant like this:
std::vector<std::variant<SubClass1, SubClass2, ...>> container2;
What are the advantages/disadvantages of using one or the other? I am interested in the performance too.
Take into consideration that I am going to sort the container by x, and I also need to be able to find out the exact type of the elements. I am going to
Fill the container,
Sort it by x,
Iterate through all the elements, find out the type, use it accordingly,
Clear the container, then the cycle starts over again.
std::variant<A,B,C> holds one of a closed set of types. You can check whether it holds a given type with std::holds_alternative, or use std::visit to pass a visitor object with an overloaded operator(). There is likely no dynamic memory allocation, however, it is hard to extend: the class with the std::variant and any visitor classes will need to know the list of possible types.
On the other hand, BaseClass* holds an unbounded set of derived class types. You ought to be holding std::unique_ptr<BaseClass> or std::shared_ptr<BaseClass> to avoid the potential for memory leaks. To determine whether an instance of a specific type is stored, you must use dynamic_cast or a virtual function. This option requires dynamic memory allocation, but if all processing is via virtual functions, then the code that holds the container does not need to know the full list of types that could be stored.
A problem with std::variant is that you need to specify a list of allowed types; if you add a future derived class you would have to add it to the type list. If you need a more dynamic implementation, you can look at std::any; I believe it can serve the purpose.
I also need to be able to find out the exact type of the elements.
For type recognition you can create a instanceof-like template as seen in C++ equivalent of instanceof. It is also said that the need to use such a mechanism sometimes reveals poor code design.
The performance issue is not something that can be detected ahead of time, because it depends on the usage: it's a matter of testing different implementations and see witch one is faster.
Take into consideration that, I am going to sort the container by x
In this case you declare the variable public so sorting is no problem at all; you may want to consider declaring the variable protected or implementing a sorting mechanism in the base class.
What are the advantages/disadvantages of using one or the other?
The same as advantages/disadvantages of using pointers for runtime type resolution and templates for compile time type resolution. There are many things that you might compare. For example:
with pointers you might have memory violations if you misuse them
runtime resolution has additional overhead (but also depends how would you use this classes exactly, if it is virtual function call, or just common member field access)
but
pointers have fixed size, and are probably smaller than the object of your class will be, so it might be better if you plan to copy your container often
I am interested in the performance too.
Then just measure the performance of your application and then decide. It is not a good practice to speculate which approach might be faster, because it strongly depends on the use case.
Take into consideration that, I am going to sort the container by x
and I also need to be able to find out the exact type of the elements.
In both cases you can find out the type. dynamic_cast in case of pointers, holds_alternative in case of std::variant. With std::variant all possible types must be explicitly specified. Accessing member field x will be almost the same in both cases (with the pointer it is pointer dereference + member access, with variant it is get + member access).
Sending data over a TCP connection was mentioned in the comments. In this case, it would probably make the most sense to use virtual dispatch.
class BaseClass {
public:
int x;
virtual void sendTo(Socket socket) const {
socket.send(x);
}
};
class SubClass1 final : public BaseClass {
public:
double y;
void sendTo(Socket socket) const override {
BaseClass::sendTo(socket);
socket.send(y);
}
};
class SubClass2 final : public BaseClass {
public:
float z;
void sendTo(Socket socket) const override {
BaseClass::sendTo(socket);
socket.send(z);
}
};
Then you can store pointers to the base class in a container, and manipulate the objects through the base class.
std::vector<std::unique_ptr<BaseClass>> container;
// fill the container
auto a = std::make_unique<SubClass1>();
a->x = 5;
a->y = 17.0;
container.push_back(a);
auto b = std::make_unique<SubClass2>();
b->x = 1;
b->z = 14.5;
container.push_back(b);
// sort by x
std::sort(container.begin(), container.end(), [](auto &lhs, auto &rhs) {
return lhs->x < rhs->x;
});
// send the data over the connection
for (auto &ptr : container) {
ptr->sendTo(socket);
}
It's not the same. std::variant is like a union with type safety. No more than one member can be visible at the same time.
// C++ 17
std::variant<int,float,char> x;
x = 5; // now contains int
int i = std::get<int>(v); // i = 5;
std::get<float>(v); // Throws
The other option is based on inheritance. All members are visible depending on which pointer you have.
Your selection will depend on if you want all the variables to be visible and what error reporting you want.
Related: don't use a vector of pointers. Use a vector of shared_ptr.
Unrelated: I'm somewhat not of a supporter of the new union variant. The point of the older C-style union was to be able to access all the members it had at the same memory place.
I am currently working on a project that requires me to have a vector storing pointers to objects of the same class but with different template values. I want to use shared_ptrs for reasons that I won't get too deep into (mainly that if I want to share a Column between two ColumnLists).
I need to be able to return a casted pointer from a function (as seen below).
So here is a very simplified version:
template <typename ColType>
class Column {
std::vector<ColType>;
};
template <typename ...TypesOfColumns>
class ColumnList {
private:
std::vector< std::shared_ptr<void> > columnsVector;
/* Needs to have a vector storing pointers to multiple Columns
all with different template values */
public:
template <typename ReturnType> std::shared_ptr<ReturnType> GetPointer(int index)
{
return std::static_pointer_cast<ReturnType>(columnsVector.at(index));
};
};
I am wondering if I am getting to some type of undefined behavior here? Will it work as I am hoping: will returning the casted type just add one to the Strong Reference Counter of the void pointer? Can one be deleted before the other one is? Am I risking a memory leak where one gets deleted and the other does not (I doubt this one to be the case)?
As always, thanks for all of the help!!!
static_pointer_cast follows the same rules that static_cast does. It is legal to downcast a pointer-to-base to a pointer-to-derived as long as derived derives from base. As long as you are sure that columnsVector.at(index) contains a shared_ptr<ReturnType> (that is, contains a shared_ptr<void> that originated as a shared_ptr<ReturnType>), what you are doing is legal. If you don't know for sure, you should use dynamic_pointer_cast.
Further, regarding memory, the output of static_pointer_cast shares the same underlying control block as the input, so it's safe that way too. Even if the input were to be deleted first, the output would remain a legal owner of the shared resource, and vice-versa.
IMO much better would be having an abstract column as the base class for the template:
struct AbstractColumn {
virtual ~AbstractColumn() {}
...
};
template<typename Type>
struct Column : AbstractColumn {
std::vector<Type> data;
};
That would provide a place where to put generic methods (e.g. conversion to string, parsing from string etc.) and will also allow you to use pointers to the base class instead of pointers to void.
I'm trying to make a vector in C++ that can store 3 different data types. I do not want to use the boost library.
Something like:
vector<type1, type2, type3> vectorName;
Do I need to make a template? And if yes how would I do that?
EDIT: as of C++17, the standard library now includes the class template std::variant, which is quite similar to pre-existing solutions in boost. variant is a type-safe alternative to unions that allows multiple types to be joined using an "or" relationship, e.g., an std::variant<type1, type2, typ3> contains either a type1 OR a type2 OR a type3. It can be composed with std::vector to yield exactly what you've described:
std::vector<std::variant<type1, type2, type3>> vectorName;
However, std::variant does introduce some restrictions. For example, it cannot hold reference or array types, and the underlying type (i.e. type1 or type2) can only be accessed by template code. If std::variant does not permit the specific behavior you need, keep reading for a more complicated but more versatile approach that also has the benefit of working in any version of C++.
ORIGINAL ANSWER:
The easiest way to store multiple types in the same vector is to make them subtypes of a parent class, wrapping your desired types in classes if they aren't classes already.
class Parent {
// Anything common to the types should be declared here, for instance:
void print() { // Make this virtual if you want subclasses to override it
std::cout << "Printing!";
}
virtual ~Parent(); //virtual destructor to ensure our subclasses are correctly deallocated
};
class Type1 : public Parent {
void type1method();
};
class Type2 : public Parent {
void type2Method();
};
class Type3 : public Parent {
void type3Method();
};
You can then create a vector of Parent pointers that can store pointers to the child types:
std::vector<Parent*> vec;
vec.push_back(new Type1);
vec.push_back(new Type2);
vec.push_back(new Type3);
When accessing elements directly from the vector, you'll only be able to use members that belong to Parent. For instance, you can write:
vec[0]->print();
But not:
vec[0]->type1Method();
As the element type has been declared as Parent* and the Parent type has no member named type1Method.
If you need to access the subtype-specific members, you can convert the Parent pointers to subtype pointers like so:
Parent *p = vec[0];
Type1 *t1 = nullptr;
Type2 *t2 = nullptr;
Type3 *t3 = nullptr;
if (t1 = dynamic_cast<Type1*>(p)) {
t1->type1Method();
}
else if (t2 = dynamic_cast<Type2*>(p)) {
t2->type2Method();
}
else if (t3 = dynamic_cast<Type3*>(p)) {
t3->type3Method();
}
Although it's generally considered a better idea to avoid this kind of explicit type-branching and instead rely on virtual methods.
Be sure to delete the pointers before removing them from the vector if you use dynamic allocation, as I did in the example above. Alternatively, use smart pointers (probably std::unique_ptr) and let your memory take care of itself:
std::vector<std::unique_ptr<Parent>> vec;
I'm trying to make a vector in C++ that can store 3 different data types.
The answer here really depends on the particular use case:
If the objects are somehow connected and similar in some fashion - create a base class and derive all the classes from it, then make the vector store unique_ptrs to the parent class (see ApproachingDarknessFish s answer for details),
If the objects are all of fundamental (so built-in) types - use an union that groups the types and define a vector<yourUnionType>,
If the objects are of unknown type, but you're sure they share a similiar interface, create a base class, and derive a templated child class from it (template <typename T> class container: public parent{};), and create a vector<unique_ptr<parent>> like in the first case,
If the objects are of types that for some reason cannot be connected (so for example the vector stores int, std::string, and yourType), connect them via an union, like in 2. Or - even better...
...if you have time and want to learn something - look how boost::any is implemented and try implementing it yourself, if you really don't want to use the library itself. It's not as hard as it may seem.
you could use std::any, store your objects in the vector as any and when you pull them out use type() == typeid(mytype)
https://en.cppreference.com/w/cpp/utility/any
This is only for C++17 onwards though.
Does it have to be a vector? You might just consider a linked list of generic type, then iterate through that list, use typeid() to figure out the data type of the node and pull the data with a node.get() function.
Function1 can be called with any type T which will be converted to (void*) to be able to add to the list but with this I lose the original pointer type (I need t store tham in one linkedlist because I cannot create one for every possible type). So somehow I need to save the type of the pointer as well. I know that it cant be done using c++. Can anyone suggest an alternative solution?
class MyClass
{
template<class T>
void function1(T* arg1)
{
myList.add((void*)arg);
}
void function2()
{
for(int i = 0; i < myList.size(); i++)
{
myList.get(i);
//restore the original pointer type
}
}
STLinkedlist<void*> myList;
}
The usual way to handle these kinds of problems is by using a public interface, in C++ this is done through inheritance. This can be a drag, especially in constrained situations, where a full class/interface hierarchy would provide too much code/runtime overhead.
In comes Boost.Variant, which allows you to use the same object to store different types. If you need even more freedom, use Boost.Any. For a comparison, see e.g. here.
At the end of the day (or better: rather sooner than later), I'd try to do things differently so you don't have this problem. It may well be worth it in the end.
If you lost the type info by going void* it is just gone. You can not just restore it.
So you either must store extra information along with the pointer, then use branching code to cast it back, or rather drive design to avoid the loss.
Your case is pretty suspicious that you do not what you really want.
A more usual case is that you want a polymorphic collection. That doesn't store any kind of pointers but those belonging to the same hierarchy. Collection has Base* pointers, and you can use the objects through Base's interface, all calling the proper virtual function without programmer's interaction. And if you really need to cast to the original type you can do it via dynamic_cast safely. Or add some type info support in the base interface.
Function1 can be called with any type T which will be converted to (void*) to be able to add to the list but with this I lose the original pointer type (I need t store tham in one linkedlist because I cannot create one for every possible type).
You're having the XY problem. The solution is not to decay your pointers to void* and store type information.
You simply can create a type for every possible type - you create a template type. You need to define an abstract interface for your "type for every object", then define a template class implementing this interface, that is particularized by type. Finally, you create your custom-type instance on your type of pointer received and store them by base class pointer (where the base class is your interface definition).
All that said, you (normally) shouldn't need to implement this at all, because the functionality is already implemented in boost::any or boost::variant (you will have to choose one of them).
General
Take into consideration, that if you want to store different objects inside a std::vector<void *>, mostly likely your application has a bad design. In this case, I'd think, whether it is really necessary to do it (or how can it be done in another way), rather than searching for the solution, how to do it.
However, there are no fully evil things in C++ (nor in any other language), so if you are absolutely certain, that this is the only solution, here are three possible ways to solve your problem.
Option 1
If you store only pointers to simple types, store the original type along with the pointer by an enum value or simply a string.
enum DataType
{
intType,
floatType,
doubleType
};
std::vector<std::pair<void *, DataType>> myData;
Option 2
If you store mixed data (classes and simple types), wrap your data in some kind of class.
class BaseData
{
public:
virtual ~BaseData() { }
};
class IntData : public BaseData
{
public:
int myData;
};
std::vector<BaseData *> myData;
Later, you'll be able to check the type of your data using dynamic_cast.
Option 3
If you store only classes, store them simply as a pointer to their base class and dynamic_cast your way out.
You could use boost::any to store any type in your list instead of use void*. It's not exactly what you want but I don't think you can restore the type in run time (as Kerrek said, it's not Java).
class MyClass
{
template<class T>
void function1(T arg1)
{
myList.add(arg);
}
template<class T>
T get(int i)
{
return boost::any_cast<T>(myList.get(i));
}
STLinkedlist<boost::any> myList;
};
Say I have two different objects that are completely different, Sprite and PhysicsData.
I write an empty base class and a container class that can push and remove objects to/from a container.
I create two of these containers to store the two different objects - Sprite and PhysicsData.
(Different objects aren't together in the same class)
class base
{
};
class ContainerManager
{
public:
std::vector<base*> list;
void Push(base *object);
void Remove(base *object);
};
class PhysicsData : public base
{
void applyGravity();
};
class Sprite : public base
{
void Draw();
};
ContainerManager SpriteContainer;
ContainerManager PhysicsDataContainer;
Sprite aSprite;
SpriteContainer.Push(&aSprite);
PhysicsData SomeData;
PhysicsDataContainer.Push(&SomeData);
Is this the way that this should bet done?
This is not a C++ way. You should use templates.
For you to know, STL (which you are calling to when using std:: namespace prefix) is, actually, Standard Template Library :). A lot of template classes are already there, e.g. for the push / remove operations see std::list<T>
You use it like this:
std::list<Sprite> SpriteContainer;
std::list<PhysicsData> PhysicsDataContainer;
and so on.
There is a cool guide about C++ templates, if you still want to do your own class for some more functionality.
And there is a reference to std::list, (i don't think i need to explain the usage of std::vector for you) if the question was the actual thing you've wanted to do.
You're having Templates in C++ and still worrying about having a common base class for a trivial container??
template <class T>
class Container{
private:
vector<T> list;
public:
void Push(T data);
T Pop();
};
If you put a single virtual function into the base class, you'll be able to use dynamic_cast to get back the proper pointer if you mix different types in the same container. A virtual destructor would be a good idea because then you could delete the object if it were dynamically allocated, without having to cast it back to the original pointer.
If you're not going to mix types within a single container, I agree with dreamzor that a template class would be better. That's the way all the standard containers are implemented.
That looks like technically correct code but you are not doing any type checking. So as you can see PhysicsData is showing up in your SpriteContainer. You probably do not want this.
There is more than one way to keep PhysicsData out of your SpriteContainer. One way is to use templates. With templates you would state what type of base objects the container should work with at compile time.
Another method is to inherit from your ContainerManager two types of ContainerManagers, one called PhysicsContainerManager and one called SpriteContainerManager. These two child classes can do type check to verify that the objects being passed are in fact either a SpriteContainer or a PhisicsData. This link shows how to do type check in c++ C++ equivalent of instanceof
thanks
jose