I want to implement a resource loader, and conceptually, it feels like all the resources in SDL2 are the same; you need to free the resource when finished, SDL_Texture* with SDL_DestroyTexture, Mix_Music* with Mix_FreeMusic, Mix_Chunk* with Mix_FreeChunk, TTF_Font* with TTF_CloseFont. All that changes is the name of the "deleter" function, so I would like to box these all so I don't need 4 different std::maps for each type of resource.
I've implemented a small class that boxes the data, but I'm having trouble getting the types back when using generics. Specifically, I get "SDL_Texture* is an incomplete type" when I try to cast the void* back to SDL_Texture* via value.get<SDL_Texture*>()
ValueBox.h
// helper class used to box various pointers for sdl, like textures, chunks, fonts, etc
class ValueBox {
public:
std::function<void(void)> clean;
void* data;
ValueBox(void* data, std::function<void(void)> clean) : data(data), clean( std::move(clean) ) {}
~ValueBox() {
clean();
}
template<typename T>
T get() {
return dynamic_cast<T>(data);
}
};
How do I implement a class that allows me to box the pointers so that I don't need four different maps in the loader? (Or am I doing something that I shouldn't?)
dynamic_cast only makes sense when you're casting to a polymorphic type (or void*), from a polymorphic type. A polymorphic type is a class (or a struct, which is formally also a class) that has (possibly inherits) at least one virtual function.
None of the types you listed are polymorphic (because they come from C libraries, and C doesn't have virtual functions). But even if they were, void itself is not polymorphic, so it wouldn't work anyway.
Additionally, you must dynamic_cast to a pointer or reference, but it doesn't matter because of the above.
Since all your resources are pointers, you can [ab]use std::unique_ptr to dispose of them automatically. Here's an example for FILE * and std::fopen:
using file_ptr = std::unique_ptr<FILE, std::integral_constant<decltype(&std::fclose), std::fclose>>;
int main()
{
file_ptr f(std::fopen("foo.txt", "rb")); // This is closed automatically.
}
However, I don't recommend doing so, because it only works with pointers. If you encounter a new type of resource that's not a pointer, you'll have to manage it differently, making your code inconsistent.
You could in theory write a class similar to std::unique_ptr that's not limited to pointers, but after trying this myself, I decided that it's not very convenient and not worth the effort.
I suggest writing an individual class for each kind of resource, using following pattern:
class FilePtr
{
FILE* file = nullptr;
public:
FilePtr() {} // Optional
FilePtr(const char *filename, const char *mode) // Change parameters as needed.
{
file = std::fopen(filename, mode);
if (!file)
throw std::runtime_error("Can't open file!");
}
FilePtr(FilePtr &&other) noexcept : file(std::exchange(other.file, {})) {}
FilePtr &operator=(FilePtr other) noexcept
{
std::swap(file, other.file);
return *this;
}
~FilePtr()
{
if (file)
std::fclose(file);
)
[[nodiscard]] explicit operator bool() const {return bool(file);} // Optional.
// Add more functions as needed.
};
Since those wrappers are so simple, you can easily write them for each kind of resource.
Having individual classes also allows you to add resource-specific functions to them.
class that allows me to box the pointers so that I don't need four different maps in the loader?
I would use different maps. This means you don't need to validate resource type at runtime, meaning one less failure point.
But regardless of the number of maps, you can inherit your wrapper classes from a single base, to reduce code duplication.
If you make the base polymorphic, you'll be able to store the resources in a single map of shared_ptrs (or unique_ptrs) to that base.
Related
I have a custom container class that is templated:
template<typename T>
class MyContainer {
T Get();
void Put(T data);
};
I would like to pass a pointer to this container to a function that will access the container's data as generic data - i.e. char* or void*. Think serialization. This function is somewhat complicated so it would be nice to not specify it in the header due to the templates.
// Errors of course, no template argument
void DoSomething(MyContainer *container);
I'm ok with requiring users to provide a lambda or subclass or something that performs the conversion. But I can't seem to come up with a clean way of doing this.
I considered avoiding templates altogether by making MyContainer hold a container of some abstract MyData class that has a virtual void Serialize(void *dest) = 0; function. Users would subclass MyData to provide their types and serialization but that seems like it's getting pretty complicated. Also inefficient since it requires storing pointers to MyData to avoid object slicing and MyData is typically pretty small and the container will hold large amounts (a lot of pointer storage and dereferencing).
You don't need any char* or void* or inheritance.
Consider this simplified implementation:
template <class T>
void Serialize (std::ostream& os, const MyContainer<T>& ct) {
os << ct.Get();
}
Suddenly this works for any T that has a suitable operator<< overload.
What about user types that don't have a suitable operator<< overload? Just tell the users to provide one.
Of course you can use any overloaded function. It doesn't have to be named operator<<. You just need to communicate its name and signature to the users and ask them to overload it.
You can introduce a non-template base class for the container with a pure virtual function that returns a pointer to raw data and implement it in your container:
class IDataHolder
{
public:
virtual ~IDataHolder(); // or you can make destructor protected to forbid deleteing by pointer to base class
virtual const unsigned char* GetData() const = 0;
};
template<typename T>
class MyContainer : public IDataHolder
{
public:
T Get();
void Put(T data);
const unsigned char* GetData() const override { /* cast here internal data to pointer to byte */}
};
void Serialize(IDataHolder& container)
{
const auto* data = container.GetData();
// do the serialization
}
I would like to pass a pointer to this container to a function that will access the container's data as generic data - i.e. char* or void*. Think serialization.
Can't be done in general, because you don't know anything about T. In general, types cannot be handled (e.g. copied, accessed, etc.) as raw blobs through a char * or similar.
Therefore, you would need to restrict what T can be, ideally enforcing it, otherwise never using it for Ts that would trigger undefined behavior. For instance, you may want to assert that std::is_trivially_copyable_v<T> holds. Still, you will have to consider other possible issues when handling data like that, like endianness and packing.
This function is somewhat complicated so it would be nice to not specify it in the header due to the templates.
Not sure what you mean by this. Compilers can handle very easily headers, and in particular huge amounts of template code. As long as you don't reach the levels of e.g. some Boost libraries, your compile times won't explode.
I considered avoiding templates altogether by making MyContainer hold a container of some abstract MyData class that has a virtual void Serialize(void *dest) = 0; function. Users would subclass MyData to provide their types and serialization but that seems like it's getting pretty complicated. Also inefficient since it requires storing pointers to MyData to avoid object slicing and MyData is typically pretty small and the container will hold large amounts (a lot of pointer storage and dereferencing).
In general, if you want a template, do a template. Using dynamic dispatching for this will probably kill performance, specially if you have to go through dispatches for even simple types.
As a final point, I would suggest taking a look at some available serialization libraries to see how they achieved it, not just in terms of performance, but also in terms of easy of use, integration with existing code, etc. For instance, Boost Serialization and Google Protocol Buffers.
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 6 years ago.
Improve this question
I'm currently working on my master thesis and it seems I cannot find a satisfying solution to the following problem. The idea here is to design a small library that should abstract from the underlying APIs (such as DirectX11 and OpenGL4). It is not required for two or more APIs to coexist in the same application, so I could theoretically write a bunch of preprocessor directives to discriminate among them, however this will blow my code and, of course, it is not extensible at all.
The abstract factory seems very handy, however it seems that I cannot find a way to make it work with templates.
Let's start...
I have an abstract class Factory whose purpose is to instantiate the objects needed by the application to work, such as Resources and Context. The former is used to load resources at runtime, while the latter is used to render the 3D scene.
Both Resources and Context are abstract because their implementation depends on the underlying API.
class Factory{
public:
virtual Resources & GetResources() = 0;
virtual Context & GetContext() = 0;
}
Resources class will load the resources needed, and return objects of type Texture2D and Mesh. Again, those classes are abstract since they depend on the specific API.
Let's say that i'm working with DirectX11 and OpenGL4.5. For each of those APIs I have the classes above derived, respectively DX11Factory, DX11Resources, DX11Context, DX11Texture2D, DX11Mesh and so on. The class they extend are pretty obvious. Fair enough.
The trivial way to design the interface of the class Resource is the following:
class Resources{
public:
Texture2D LoadTexture(const wstring & path) = 0;
Mesh LoadMesh(const wstring & path) = 0;
}
The class DX11Resource will implement the methods above and everything would work just fine... except that if I wanted to support a new resource type in the future like TextureCube (and from a software engineer point of view, I will for sure. Right now I don't care), I'll have to declare a new method TextureCube LoadTextureCube(...) in the interface the library user will actually use, that is Resources. This will mean that I`ll have to implement that method in every single derived class (open-closed principle FTW!).
My very first idea to solve this problem was the following:
class Texture2D{...}
class Resources{
public:
template<typename TResource>
virtual TResource Load(const wstring & path) = 0; // :(
}
namespace dx11{
class DX11Texture2D: public Texture2D{...}
class DX11Texture2DLoader{...}
template<typename TResource> struct resource_traits;
template<> struct resource_traits<Texture2D>{
using type = DX11Texture2D;
using loader = DX11Texture2DLoader; //Functor type
}
class DX11Resources{
public:
template<typename TResource>
virtual TResource Load(const wstring & path){
return typename resource_traits<TResource>::loader()( path );
}
}
}
So if I need to support a new type of resource I could simply declare a new resource_traits inside the proper namespace (and of course the new resource abstract and concrete type) and everything would work. Unfortunately virtual template methods are not supported (and for a very good reason, imagine what would happen writing something like this
Resources * r = GrabResources(); //It will return a DirectX9 object
r->Load<HullShader>(L"blah"); //DX9 doesn't support HullShaders, thus have no resource_traits<HullShader>
So basically the compiler won't be able to perform a proper substitution and it will point out an error to a class the user wasn`t even aware of.
)
I have thought about other solutions but none of them satisfy my needs:
1. CRTP
I can use this:
template <typename TDerived>
class Resources{
public:
template <typename TResource>
TResource Load(const wstring & path){
return typename TDerived::resource_traits<TResource>::loader()( path );
}
}
I think this will work, however Resources<TDerived> cannot be returned by the Factory object simply because TDerived is not known (and the final programmer shouldn`t anyway).
2. RTTI
class Resources{
template <typename TResource>
TResource Load(const wstring & path){
return *static_cast<TResource *>( Load(path, typeid(TResource).hash_code()) );
}
virtual void * Load(const wstring & path, size_t hash) = 0;
}
In the derived class I have to implement the pure virtual method above and then, using an if-then-else cascade I can instantiate the resource I need or return a nullptr if that particular API doesn't support it. This will work for sure but it is ugly and of course it forces me to rewrite the implementation whenever I want to support a new resource type (but at least it will be just one class)!
if( hash == typeid(Texture2D).hash_code()) // instantiate a DX11Texture2D
else if (...)...
3. Visitor
Taking advantage of the Visitor pattern. This method acttually won't help me at all, but I leave it here just in case (I always think about the visitor whenever I see a never-ending if-then-else cascade with integrated downcast, like in the previous point :) ).
template <typename TResource> resource_traits;
template<> resource_traits<Texture2D>{
using visitable = Texture2DVisitable;
}
struct Texture2DVisitable{
Texture2D operator()(const wstring & path, Loader & visitor){
return visitor.Load(path, *this);
}
}
template<typename TResource>
TResource Resources::Load(path){
return typename resource_traits<TResource>::visitable()(path, *this);
}
Using this approach Resources now have to declare a pure virtual method for every resource it can load like Texture2D Resources::Load(path, Texture2DVisitable &) = 0. So, again, in case of new resources I have to update the entire hierarchy accordingly... at this point I would use the trivial solution at the beginning.
4. Others?
Have I missed something? What approach should I prefer? I feel like i'm overcomplicating stuffs, as always!
Thanks in advance and sorry for my poorly-written wall-o-text!
ps: Get rid of the Resource class in first place is not an option since its real purpose is to prevent the loading of the same resource over and over. It is basically a huge flyweight.
This problem really boils down to the whole "virtual function template" problem. Basically, the solution (whatever it is) has to take a compile-time information (e.g., template argument), turn it into run-time information (e.g., value, type-id, hash-code, function-pointer, etc.), go past the run-time dispatch (virtual call), and then turn that run-time information back into compile-time information (e.g., which piece of code to execute). By understanding this, you'll realize that the most direct solution is to use that "RTTI" solution, or a variation thereof.
As you point out, the only real problem with that solution is that it is "ugly". I agree that it is a bit ugly and that other than that, it's a nice solution, especially the fact that the modifications needed when adding a new supported type is localized only to the implementation (cpp files) associated to the class that you are adding that support to (you really could not hope for anything better than that).
As for the ugliness, well, that's something you can always improve on with some trickery, but there will always be some ugliness to it, especially the static_cast which cannot be removed because you need a way to emerge from the run-time dispatch back to a statically typed result. Here is a possible solution that relies on the std::type_index:
// Resources.h:
class Resources {
public:
template <typename TResource>
TResource Load(const wstring & path){
return *static_cast<TResource *>(Load(path, std::type_index(typeid(TResource))));
}
protected:
virtual void* Load(const wstring & path, std::type_index t_id) = 0;
}
// DX11Resources.h:
class DX11Resources : public Resources {
protected:
void* Load(const wstring & path, std::type_index t_id);
};
// DX11Resources.cpp:
template <typename TResource>
void* DX11Res_Load(DX11Resources& res, const wstring & path) { };
template <>
void* DX11Res_Load<Texture2D>(DX11Resources& res, const wstring & path) {
// code to load Texture2D
};
// .. so on for other things..
void* DX11Resources::Load(const wstring & path, std::type_index t_id) {
typedef void* (*p_load_func)(DX11Resources&, const wstring&);
typedef std::unordered_map<std::type_index, p_load_func> MapType;
#define DX11RES_SUPPORT_LOADER(TYPENAME) MapType::value_type(std::type_index(typeid(TYPENAME)), DX11Res_Load<TYPENAME>)
static MapType func_map = {
DX11RES_SUPPORT_LOADER(Texture2D),
DX11RES_SUPPORT_LOADER(Texture3D),
DX11RES_SUPPORT_LOADER(TextureCube),
//...
};
#undef DX11RES_SUPPORT_LOADER
auto it = func_map.find(t_id);
if(it == func_map.end())
return nullptr; // or throw exception, whatever you prefer.
return it->second(*this, path);
};
There are some variations to this (such as having member functions instead of free functions for the loaders, or using non-template functions instead of specialization, or both of these modifications), but the basic idea is that to add a new supported type, all you do is add it to the list of supported types (the DX11RES_SUPPORT_LOADER(SomeType)) to the list and create the code as a new function (only in the cpp file). There is still a bit of ugliness in there, but the header file is clean, and the ugliness in the virtual "Load" is "O(1)" in complexity, meaning that you don't add ugliness for every new type, it's constant bit of ugly code (instead of the if-else sequence, where the amount of ugly code is proportional to the number of types supported). Also, this has the side benefit of being faster in doing the dispatching (with hash table). Also, using type_index is important to avoid collisions with the hash values of two types (you don't lose the info on which typeid was used to create the hash value).
So, all in all, my recommendation is to go with the "RTTI" solution, and do what you can or want to remove some of the ugliness or inefficiencies associated to it. The most important thing is to keep the interface (header, class declaration) of the derived class as clean as possible to avoid having to add anything to it in the future (you definitely don't want that class to expose, in its declaration, what types of resources it supports via function declarations or something, otherwise, you have to recompile the world every time to add one).
N.B.: If you need to avoid using the RTTI (e.g., -fno-rtti option), then there are ways to work around that problem, but it's out of the scope of this question.
I am implementing a task runtime system that maintains buffers for user-provided objects of various types. In addition, all objects are wrapped before they are stored into the buffers. Since the runtime doesn't know the types of objects that the user will provide, the Wrapper and the Buffer classes are templated:
template <typename T>
class Wrapper {
private:
T mdata;
public:
Wrapper() = default;
Wrapper(T& user_data) : mdata(user_data) {}
T& GetData() { return mdata; }
...
};
template <typename T>
class Buffer {
private:
std::deque<Wrapper<T>> items;
public:
void Write(Wrapper<T> wd) {
items.push_back(wd);
}
Wrapper<T> Read() {
Wrapper<T> tmp = items.front();
items.pop_front();
return tmp;
}
...
};
Now, the runtime system handles the tasks, each of which operates on a subset of aforementioned buffers. Thus, each buffer is operated by one or more tasks. This means that a task must keep references to the buffers since the tasks may share buffers.
This is where my problem is:
1) each task needs to keep references to a number of buffers (this number is unknown in compile time)
2) the buffers are of different types (based on the templeted Buffer class).
3) the task needs to use these references to access buffers.
There is no point to have a base class to the Buffer class and then use base class pointers since the methods Write and Read from the Buffer class are templeted and thus cannot be virtual.
So I was thinking to keep references as void pointers, where the Task class would look something like:
class Task {
private:
vector<void *> buffers;
public:
template<typename T>
void AddBuffer(Buffet<T>* bptr) {
buffers.push_back((void *) bptr);
}
template<typename T>
Buffer<T>* GetBufferPtr(int index) {
return some_way_of_cast(buffers[index]);
}
...
};
The problem with this is that I don't know how to get the valid pointer from the void pointer in order to access the Buffer. Namely, I don't know how to retain the type of the object pointed by buffers[index].
Can you help me with this, or suggest some other solution?
EDIT: The buffers are only the implementation detail of the runtime system and the user is not aware of their existence.
In my experience, when the user types are kept in user code, run-time systems handling buffers do not need to worry about the actual type of these buffer. Users can invoke operations on typed buffers.
class Task {
private:
vector<void *> buffers;
public:
void AddBuffer(char* bptr) {
buffers.push_back((void *) bptr);
}
char *GetBufferPtr(int index) {
return some_way_of_cast(buffers[index]);
}
...
};
class RTTask: public Task {
/* ... */
void do_stuff() {
Buffer<UserType1> b1; b1Id = b1.id();
Buffer<UserType2> b2; b2Id = b2.id();
AddBuffer(cast(&b1));
AddBuffer(cast(&b2));
}
void do_stuff2() {
Buffer<UserType1> *b1 = cast(GetBufferPtr(b1Id));
b1->push(new UserType1());
}
};
In these cases casts are in the user code. But perhaps you have a different problem. Also the Wrapper class may not be necessary if you can switch to pointers.
What you need is something called type erasure. It's way to hide the type(s) in a template.
The basic technique is the following:
- Have an abstract class with the behavior you want in declared in a type independent maner.
- Derive your template class from that class, implement its virtual methods.
Good news, you probably don't need to write your own, there boost::any already. Since all you need is get a pointer and get the object back, that should be enough.
Now, working with void* is a bad idea. As perreal mentioned, the code dealing with the buffers should not care about the type though. The good thing to do is to work with char*. That is the type that is commonly used for buffers (e.g. socket apis). It is safer than too: there is a special rule in the standard that allows safer conversion to char* (see aliasing rules).
This isn't exactly an answer to your question, but I just wanted to point out that the way you wrote
Wrapper<T> Read() {
makes it a mutator member function which returns by value, and as such, is not good practice as it forces the user write exception unsafe code.
For the same reason the STL stack::pop() member function returns void, not the object that was popped off the stack.
I am looking for some design advices for the following problem:
I am using boost geometry, I have a couple of custom geometry types compatible with boost geometry (via traits), but most of the types I am using are typedefs.
class MyPoint
{
// custom stuff
};
// declare traits for MyPoint for use wih boost geometry here
class MyTaggedPoint : public MyPoint
{
// more custom stuff
};
// declare traits for MyTaggedPoint for use wih boost geometry here
// example typedefs
typedef boost::geometry::model::polygon<MyPoint> Polygon;
typedef boost::geometry::model::polygon<MyTaggedPoint> TaggedPolygon;
My problem is when I want to serialize/deserialize my geometries.
Let's say all geometries are stored in a binary field in a database. If I would have a base geometry class, I would probably just write g->type() (4 bytes) and call g->save(some_outputstream) and write all of that to the binary field. Then when reading the binary field I would simply read the bytes and cast to appropriate geometry type.
But Boost geometries do not have a common base class.
How do you guys usually approach serialization when there are multiple types that can be stored as binary and you do not have a shared base class ?
I was thinking of maybe having a Serializer class, that returns a boost.Any and then the geometry can be casted afterward with the type that would be stored in the (de)serializer? But then the serializer would need a save method for each geometry types ? ex: Save(myPolygon), Save(myPoint)
Any ideas/experiences?
Boost's serialization supports non-invasive serialization if you do not wish to reimplement the wheel. You may even be able to find library support for their geometry types somewhere. The interface is somewhat complicated due to XML concerns unfortunately.
To serialize objects to and from bytes, you ultimately need 2 functions for EACH type you have to support (primatives, objects, etc.). These are "Load()" and "Store()".
Ideally, you use a fixed interface for the bytes- a iostream, char*, some buffer object- etc.
For the sake of readability let's call it "ByteBuffer", since logically that's what its role is.
We now have something like template functions for the Serializable concept:
template<typename T>
ByteBuffer Store(const T& object) { // BUT, What goes here...? }
template<typename T>
T Load(const ByteBuffer& bytes);
Okay, this isn't going to work for anything other than the primitive types- even if we made these "visitors" or something they literally have to know every detail about the object's internals to do their job. Furthermore, "Load()" is logically a constructor (really, a FACTORY since it could easily fail). We've got to associate these with the actual objects.
To make Serializable a base class, we need to use the "curiously recurring template" pattern. To do this, we require all derived classes to have a constructor of the form:
T(const ByteBuffer& bytes);
To check for errors, we can provide a protected flag "valid" in the base class that derived constructors can set. Note that your object has to support factory-style construction anyway for Load() to work well with it.
Now we can do this right, providing "Load" as a factory:
template<typename T>
class Serializable // If you do reference-counting on files & such, you can add it here
{
protected:
bool valid;
// Require derived to mark as valid upon load
Serializable() : valid(false) {}
virtual ~Serializable() { valid = false; }
public:
static T Load(const ByteBuffer& bytes); // calls a "T(bytes)" constructor
// Store API
virtual ByteBuffer Store() = 0; // Interface details are up to you.
};
Now, just derive from the base class like so and you can pick up everything you need:
class MyObject : public Serializable<MyObject>
{
protected:
// .. some members ...
MyObject(const ByteBuffer& bytes)
{
//... Actual load logic for this object type ...
// On success only:
valid = true;
}
public:
virtual ByteBuffer Store() {
//... store logic
}
};
What's cool is that you can call "MyObject::Load()" and it'll do exactly what you expect. Futhermore, "Load" can be made into the ONLY way to build the object, allowing you clean APIs for read-only files and such.
Extending this to full File APIs takes a little more work, namely adding a "Load()" that can read from a larger buffer (holding other things) and "Store()" that appends to an existing buffer.
As a side note, do NOT use boost's APIs for this. In a good design serializable objects should map 1-to-1 to packed structures of primitive types on disk- that's the only way the resulting files are really going to be usable by other programs or on other machines. Boost gives you a horrible API that mostly enables you to do things you'll regret later.
What is a common practice for the storage of a list of base class pointers each of which can describe a polymorphic derived class?
To elaborate and in the interest of a simple example lets assume that I have a set of classes with the following goals:
An abstract base class whose purpose is to enforce a common functionality on its derived classes.
A set of derived classes which: can perform a common functionality, are inherently copyable (this is important), and are serializable.
Now alongside this required functionality I want to address the following key points:
I want the use of this system to be safe; I don't want a user to have undefined errors when he/she erroneously casts a base class pointer to the wrong derived type.
Additionally I want as much as possible the work for copying/serializing this list to be taken care of automatically. The reason for this is, as a new derived type is added I don't want to have to search through many source files and make sure everything will be compatible.
The following code demonstrates a simple case of this, and my proposed (again I am looking for a common well thought out method of doing this, mine may not be so good) solution.
class Shape {
public:
virtual void draw() const = 0;
virtual void serialize();
protected:
int shapeType;
};
class Square : public Shape
{
public:
void draw const; // draw code here.
void serialize(); // serialization here.
private:
// square member variables.
};
class Circle : public Shape
{
public:
void draw const; // draw code here.
void serialize(); // serialization here.
private:
// circle member variables.
};
// The proposed solution: rather than store list<shape*>, store a generic shape type which
// takes care of copying, saving, loading and throws errors when erroneous casting is done.
class GenericShape
{
public:
GenericShape( const Square& shape );
GenericShape( const Circle& shape );
~GenericShape();
operator const Square& (); // Throw error here if a circle tries to get a square!
operator const Circle& (); // Throw error here if a square tries to get a circle!
private:
Shape* copyShape( const Shape* otherShape );
Shape* m_pShape; // The internally stored pointer to a base type.
};
The above code is certainly missing some items, firstly the base class would have a single constructor requiring the type, the derived classes would internally call this during their construction. Additionally in the GenericShape class, copy/assignment constructor/operator would be present.
Sorry for the long post, trying to explain my intents fully. On that note, and to re-iterate: above is my solution, but this likely has some serious flaws and I would be happy to hear about them, and the other solutions out there!
Thank you
What is the problem of a std::list< shape* > (or a std::list< boost::shared_ptr > thereof)?
That would be the idiomatic way of implementing a list of shapes with polymorphic behavior.
I want the use of this system to be safe; I don't want a user to have undefined errors when he/she erroneously casts a base class pointer to the wrong derived type.
Users should not downcast, but rather use the polymorphism and the base (shape) operations provided. Consider why they would be interested in downcasting, if you find a reason to do so, go back to drawing board and redesign so that your base provides all needed operations.
Then if the user wants to downcast, they should use dynamic_cast, and they will get the same behavior you are trying to provide in your wrapper (either a null pointer if downcasting pointers or a std::bad_cast exception for reference downcasting).
Your solution adds a level of indirection and (with the provided interface) require the user to try guessing the type of shape before use. You offer two conversion operators to each of the derived classes, but the user must call them before trying to use the methods (that are no longer polymorphic).
Additionally I want as much as possible the work for copying/serializing this list to be taken care of automatically. The reason for this is, as a new derived type is added I don't want to have to search through many source files and make sure everything will be compatible.
Without dealing with deserialization (I will come back later), your solution, as compared to storing (smart) pointers in the list, requires revisiting the adapter to add new code for each and every other class that is added to the hierarchy.
Now the deserialization problem.
The proposed solution is using a plain std::list< boost::shared_ptr >, once you have the list built, drawing and serialization can be performed right out of the box:
class shape
{
public:
virtual void draw() = 0;
virtual void serialize( std::ostream& s ) = 0;
};
typedef std::list< boost::shared_ptr<shape> > shape_list;
void drawall( shape_list const & l )
{
std::for_each( l.begin(), l.end(), boost::bind( &shape::draw, _1 ));
}
void serialize( std::ostream& s, shape_list const & l )
{
std::for_each( l.begin(), l.end(), boost::bind( &shape::serialize, _1, s ) );
}
Where I have used boost::bind to reduce code bloat instead of iterating manually. The problem is that you cannot virtualize construction as before the object has been constructed you cannot know what type it actually is. After the problem of deserializing one element of a known hierarchy is solved, deserializing the list is trivial.
Solutions to this problem are never as clean and simple as the code above.
I will assume that you have defined unique shape type values for all shapes, and that your serialization starts by printing out that id. That is, the first element of serialization is the type id.
const int CIRCLE = ...;
class circle : public shape
{
// ...
public:
static circle* deserialize( std::istream & );
};
shape* shape_deserialize( std::istream & input )
{
int type;
input >> type;
switch ( type ) {
case CIRCLE:
return circle::deserialize( input );
break;
//...
default:
// manage error: unrecognized type
};
}
You can further alleviate the need to work on the deserializer function if you convert it into an abstract factory where upon creation of a new class the class itself registers it's deserialization method.
typedef shape* (*deserialization_method)( std::istream& );
typedef std::map< int, deserialization_method > deserializer_map;
class shape_deserializator
{
public:
void register_deserializator( int shape_type, deserialization_method method );
shape* deserialize( std::istream& );
private:
deserializer_map deserializers_;
};
shape* shape_deserializator::deserialize( std::istream & input )
{
int shape_type;
input >> shape_type;
deserializer_map::const_iterator s = deserializers_.find( shape_type );
if ( s == deserializers_.end() ) {
// input error: don't know how to deserialize the class
}
return *(s->second)( input ); // call the deserializer method
}
In real life, I would have used boost::function<> instead of the function pointers, making the code cleaner and clearer, but adding yet another dependency to the example code. This solution requires that during initialization (or at least before trying to deserialize) all classes register their respective methods in the shape_deserializator object.
You could avoid lots of repetition in GenericShape by using templates (for the constructors and converters), but the key bit that's missing is having it inherit from Shape and implement its virtuals -- without it it's unusable, with it it's a pretty normal variant on envelope/implementation idioms.
You may want to use auto_ptr (or somewhat-smarter pointers) rather than a bare pointer to Shape, too;-).
I would propose boost::shared_pointer<Shape> in an STL container. Then use dynamic_cast to downcast guarantee type correctness. If you want to provide helper functions to toss exceptions instead of returning NULL, then follow Alex's suggestion and define a template helper function like:
template <typename T, typename U>
T* downcast_to(U *inPtr) {
T* outPtr = dynamic_cast<T*>(inPtr);
if (outPtr == NULL) {
throw std::bad_cast("inappropriate cast");
}
return outPtr;
}
and use it like:
void some_function(Shape *shp) {
Circle *circ = downcast_to<Circle>(shp);
// ...
}
Using a separate class like GenericShape is just too strongly coupled with every class that descends from Shape. I wonder if this would be considered a code smell or not...
I want the use of this system to be
safe; I don't want a user to have
undefined errors when he/she
erroneously casts a base class pointer
to the wrong derived type.
Why would you get undefined errors? The behavior of dynamic_cast is perfectly well-defined and catches the error if you cast a base class pointer to the wrong derived type. This really seems like reinventing the wheel.
Additionally I want as much as
possible the work for
copying/serializing this list to be
taken care of automatically. The
reason for this is, as a new derived
type is added I don't want to have to
search through many source files and
make sure everything will be
compatible.
I'm not sure what the problem is here. If all the derived classes are serializable and copyable, isn't that good enough? What more do you need?
I'm also not sure what to make of the first two requirements.
What do you mean, the ABC should "enforce a common functionality"? And what is the point in having derived classes, if their role is only to perform that same common functionality, be copyable and serializable?
Why not just make one non-abstract class serializable and copyable then?
I'm probably missing something vital here, but I don't really think you've explained what it is you're trying to achieve.