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.
Related
I am wrapping a library which I did not write to make it more user friendly. There are a huge number of functions which are very basic so it's not ideal to have to wrap all of these when all that is really required is type conversion of the results.
A contrived example:
Say the library has a class QueryService, it has among others this method:
WeirdInt getId() const;
I'd like a standard int in my interface however, I can get an int out of WeirdInt no problem as I know how to do this. In this case lets say that WeirdInt has:
int getValue() const;
This is a very simple example, often the type conversion is more complicated and not always just a call to getValue().
There are literally hundreds of function calls that return types likes these and more are added all the time, so I'd like to try and reduce the burden on myself having to constantly add a bajillion methods every time the library does just to turn WeirdType into type.
I want to end up with a QueryServiceWrapper which has all the same functionality as QueryService, but where I've converted the types. Am I going to have to write an identically names method to wrap every method in QueryService? Or is there some magic I'm missing? There is a bit more to it as well, but not relevant to this question.
Thanks
The first approach I'd think is by trying with templates such that
you provide a standard implementation for all the wrapper types which have a trivial getValue() method
you specialize the template for all the others
Something like:
class WeirdInt
{
int v;
public:
WeirdInt(int v) : v(v) { }
int getValue() { return v; }
};
class ComplexInt
{
int v;
public:
ComplexInt(int v) : v(v) { }
int getValue() { return v; }
};
template<typename A, typename B>
A wrap(B type)
{
return type.getValue();
}
template<>
int wrap(ComplexInt type)
{
int v = type.getValue();
return v*2;
};
int x = wrap<int, WeirdInt>(WeirdInt(5));
int y = wrap<int, ComplexInt>(ComplexInt(10));
If the wrapper methods for QueryService have a simple pattern, you could also think of generating QueryServiceWrapper with some perl or python script, using some heuristics. Then you need to define some input parameters at most.
Even defining some macros would help in writing this wrapper class.
Briefly, If your aim is to encapsulate the functionality completely so that WeirdInt and QueryService are not exposed to the 'client' code such that you don't need to include any headers which declare them in the client code, then I doubt the approach you take will be able to benefit from any magic.
When I've done this before, my first step has been to use the pimpl idiom so that your header contains no implementation details as follows:
QueryServiceWrapper.h
class QueryServiceWrapperImpl;
class QueryServiceWrapper
{
public:
QueryServiceWrapper();
virtual ~QueryServiceWrapper();
int getId();
private:
QueryServiceWrapperImpl impl_;
};
and then in the definition, you can put the implementation details, safe in the knowledge that it will not leach out to any downstream code:
QueryServiceWrapper.cpp
struct QueryServiceWrapperImpl
{
public:
QueryService svc_;
};
// ...
int QueryServiceWrapper::getValue()
{
return impl_->svc_.getId().getValue();
}
Without knowing what different methods need to be employed to do the conversion, it's difficult add too much more here, but you could certainly use template functions to do conversion of the most popular types.
The downside here is that you'd have to implement everything yourself. This could be a double edged sword as it's then possible to implement only that functionality that you really need. There's generally no point in wrapping functionality that is never used.
I don't know of a 'silver bullet' that will implement the functions - or even empty wrappers on the functions. I've normally done this by a combination of shell scripts to either create the empty classes that I want or taking a copy of the header and using text manipulation using sed or Perl to change original types to the new types for the wrapper class.
It's tempting in these cases to use public inheritance to enable access to the base functions while allowing functions to be overridden. However, this is not applicable in your case as you want to change return types (not sufficient for an overload) and (presumably) you want to prevent exposure of the original Weird types.
The way forward here has to be to use aggregation although in such as case there is no way you can easily avoid re-implementing (some of) the interfaces unless you are prepared to automate the creation of the class (using code generation) to some extent.
more complex approach is to introduce a required number of facade classes over original QueryService, each of which has a limited set of functions for one particular query or query-type. I don't know that your particular QueryService do, so here is an imaginary example:
suppose the original class have a lot of weired methods worked with strange types
struct OriginQueryService
{
WeirdType1 query_for_smth(...);
WeirdType1 smth_related(...);
WeirdType2 another_query(...);
void smth_related_to_another_query(...);
// and so on (a lot of other function-members)
};
then you may write some facade classes like this:
struct QueryFacade
{
OriginQueryService& m_instance;
QueryFacade(OriginQueryService* qs) : m_instance(*qs) {}
// Wrap original query_for_smth(), possible w/ changed type of
// parameters (if you'd like to convert 'em from C++ native types to
// some WeirdTypeX)...
DesiredType1 query_for_smth(...);
// more wrappers related to this particular query/task
DesiredType1 smth_related(...);
};
struct AnotherQueryFacade
{
OriginQueryService& m_instance;
AnotherQueryFacade(OriginQueryService* qs) : m_instance(*qs) {}
DesiredType2 another_query(...);
void smth_related_to_another_query(...);
};
every method delegate call to m_instance and decorated w/ input/output types conversion in a way you want it. Types conversion can be implemented as #Jack describe in his post. Or you can provide a set of free functions in your namespace (like Desired fromWeird(const Weired&); and Weired toWeired(const Desired&);) which would be choosen by ADL, so if some new type arise, all that you have to do is to provide overloads for this 2 functions... such approach work quite well in boost::serialization.
Also you may provide a generic (template) version for that functions, which would call getValue() for example, in case if lot of your Weired types has such member.
What are the advantages/disadvantages of the two techniques in comparison ? And more importantly: Why and when should one be used over the other ? Is it just a matter of personal taste/preference ?
To the best of my abilities, I haven't found another post that explicitly addresses my question. Among many questions regarding the actual use of polymorphism and/or type-erasure, the following seems to be closest, or so it seemed, but it doesn't really address my question either:
C++ -& CRTP . Type erasure vs polymorphism
Please, note that I very well understand both techniques. To this end, I provide a simple, self-contained, working example below, which I'm happy to remove, if it is felt unnecessary. However, the example should clarify what the two techniques mean with respect to my question. I'm not interested in discussing nomenclatures. Also, I know the difference between compile- and run-time polymorphism, though I wouldn't consider this to be relevant to the question. Note that my interest is less in performance-differences, if there are any. However, if there was a striking argument for one or the other based on performance, I'd be curious to read it. In particular, I would like to hear about concrete examples (no code) that would really only work with one of the two approaches.
Looking at the example below, one primary difference is the memory-management, which for polymorphism remains on the user-side, and for type-erasure is neatly tucked away requiring some reference-counting (or boost). Having said that, depending on the usage scenarios, the situation might be improved for the polymorphism-example by using smart-pointers with the vector (?), though for arbitrary cases this may very well turn out to be impractical (?). Another aspect, potentially in favor of type-erasure, may be the independence of a common interface, but why exactly would that be an advantage (?).
The code as given below was tested (compiled & run) with MS VisualStudio 2008 by simply putting all of the following code-blocks into a single source-file. It should also compile with gcc on Linux, or so I hope/assume, because I see no reason why not (?) :-) I have split/divided the code here for clarity.
These header-files should be sufficient, right (?).
#include <iostream>
#include <vector>
#include <string>
Simple reference-counting to avoid boost (or other) dependencies. This class is only used in the type-erasure-example below.
class RefCount
{
RefCount( const RefCount& );
RefCount& operator= ( const RefCount& );
int m_refCount;
public:
RefCount() : m_refCount(1) {}
void Increment() { ++m_refCount; }
int Decrement() { return --m_refCount; }
};
This is the simple type-erasure example/illustration. It was copied and modified in part from the following article. Mainly I have tried to make it as clear and straightforward as possible.
http://www.cplusplus.com/articles/oz18T05o/
class Object {
struct ObjectInterface {
virtual ~ObjectInterface() {}
virtual std::string GetSomeText() const = 0;
};
template< typename T > struct ObjectModel : ObjectInterface {
ObjectModel( const T& t ) : m_object( t ) {}
virtual ~ObjectModel() {}
virtual std::string GetSomeText() const { return m_object.GetSomeText(); }
T m_object;
};
void DecrementRefCount() {
if( mp_refCount->Decrement()==0 ) {
delete mp_refCount; delete mp_objectInterface;
mp_refCount = NULL; mp_objectInterface = NULL;
}
}
Object& operator= ( const Object& );
ObjectInterface *mp_objectInterface;
RefCount *mp_refCount;
public:
template< typename T > Object( const T& obj )
: mp_objectInterface( new ObjectModel<T>( obj ) ), mp_refCount( new RefCount ) {}
~Object() { DecrementRefCount(); }
std::string GetSomeText() const { return mp_objectInterface->GetSomeText(); }
Object( const Object &obj ) {
obj.mp_refCount->Increment(); mp_refCount = obj.mp_refCount;
mp_objectInterface = obj.mp_objectInterface;
}
};
struct MyObject1 { std::string GetSomeText() const { return "MyObject1"; } };
struct MyObject2 { std::string GetSomeText() const { return "MyObject2"; } };
void UseTypeErasure() {
typedef std::vector<Object> ObjVect;
typedef ObjVect::const_iterator ObjVectIter;
ObjVect objVect;
objVect.push_back( Object( MyObject1() ) );
objVect.push_back( Object( MyObject2() ) );
for( ObjVectIter iter = objVect.begin(); iter != objVect.end(); ++iter )
std::cout << iter->GetSomeText();
}
As far as I'm concerned, this seems to achieve pretty much the same using polymorphism, or maybe not (?).
struct ObjectInterface {
virtual ~ObjectInterface() {}
virtual std::string GetSomeText() const = 0;
};
struct MyObject3 : public ObjectInterface {
std::string GetSomeText() const { return "MyObject3"; } };
struct MyObject4 : public ObjectInterface {
std::string GetSomeText() const { return "MyObject4"; } };
void UsePolymorphism() {
typedef std::vector<ObjectInterface*> ObjVect;
typedef ObjVect::const_iterator ObjVectIter;
ObjVect objVect;
objVect.push_back( new MyObject3 );
objVect.push_back( new MyObject4 );
for( ObjVectIter iter = objVect.begin(); iter != objVect.end(); ++iter )
std::cout << (*iter)->GetSomeText();
for( ObjVectIter iter = objVect.begin(); iter != objVect.end(); ++iter )
delete *iter;
}
And finally for testing all of the above together.
int main() {
UseTypeErasure();
UsePolymorphism();
return(0);
}
C++ style virtual method based polymorphism:
You have to use classes to hold your data.
Every class has to be built with your particular kind of polymorphism in mind.
Every class has a common binary-level dependency, which restricts how the
compiler creates the instance of each class.
The data you are abstracting must explicitly describe an interface that describes
your needs.
C++ style template based type erasure (with virtual method based polymorphism doing the erasure):
You have to use template to talk about your data.
Each chunk of data you are working on may be completely unrelated to other options.
The type erasure work is done within public header files, which bloats compile time.
Each type erased has its own template instantiated, which can bloat binary size.
The data you are abstracting need not be written as being directly dependent on your needs.
Now, which is better? Well, that depends if the above things are good or bad in your particular situation.
As an explicit example, std::function<...> uses type erasure which allows it to take function pointers, function references, output of a whole pile of template-based functions that generate types at compile time, myraids of functors which have an operator(), and lambdas. All of these types are unrelated to one another. And because they aren't tied to having a virtual operator(), when they are used outside of the std::function context the abstraction they represent can be compiled away. You couldn't do this without type erasure, and you probably wouldn't want to.
On the other hand, just because a class has a method called DoFoo, doesn't mean that they all do the same thing. With polymorphism, it isn't just any DoFoo you are calling, but the DoFoo from a particular interface.
As for your sample code... your GetSomeText should be virtual ... override in the polymorphism case.
There is no need to reference count just because you are using type erasure. There is no need not to use reference counting just because you are using polymorphsm.
Your Object could wrap T*s like how you stored vectors of raw pointers in the other case, with manual destruction of their contents (equivalent to having to call delete). Your Object could wrap a std::shared_ptr<T>, and in the other case you could have vector of std::shared_ptr<T>. Your Object could contain a std::unique_ptr<T>, equivalent to having a vector of std::unique_ptr<T> in the other case. Your Object's ObjectModel could extract copy constructors and assignment operators from the T and expose them to Object, allowing full-on value semantics for your Object, which corresponds to the a vector of T in your polymorphism case.
Here's one view: The question seems to ask how one should choose between late binding ("runtime polymorphism") and early binding ("compile-time polymorphism").
As KerrekSB points out in his comments, there are some things you can do with late binding that it just isn't realistic to do with early binding. Many uses of the Strategy pattern (decoding network I/O) or the Abstract Factory pattern (runtime-selected class factories) fall into this category.
If both approaches are viable, then choosing is a matter of the trade offs involved. In C++ applications, the main tradeoffs I see between early and late binding are implementation maintainability, binary size, and performance.
There are at least some people who feel that C++ templates in any shape or form are impossible to comprehend. Or possibly have some other, less dramatic reservation with templates. C++ templates have many little gotchas ("when do I need to use the 'typename' and 'template' keywords?"), and non-obvious tricks (SFINAE comes to mind).
Another tradeoff is optimization. When you bind early, you give the compiler more information about your program, and so it can (potentially) do a better job optimizing. When you bind late, the compiler (probably) doesn't know ahead of time as much information -- some of that information may be in other compilation units, and so the optimizer can't do as much.
Another tradeoff is program size. In C++ at least, using "compile-time polymorphism" sometimes balloons binary size, as the compiler creates, optimizes, and emits different code for each used specialization. In contrast, when binding late, there's only one code path.
It's interesting to compare the same tradeoff being made in a different context. Take web applications, where one uses (some type of) polymorphism to deal with differences between browsers, and possibly for internationalization (i18n)/localization. Now, a hand-written JavaScript web application would likely use what amounts to late binding here, by having methods which detect capabilities at runtime to figure out what to do. Libraries like jQuery take this tack.
Another approach is to write different code for each possible browser/i18n possibility. While this sounds absurd, it is far from unheard of. The Google Web Toolkit uses this approach. GWT has its "deferred binding" mechanism, used to specialize the compiler's output to different browsers and different localizations. GWT's "deferred binding" mechanism uses early binding: The GWT Java-to-JavaScript compiler figures out all possible ways the polymorphism might be needed, and spits out an entirely different "binary" for each.
The tradeoffs are similar. Wrapping your head around how you extend GWT using deferred binding can be a headache and a half; Having knowledge at compile time allows GWT's compiler to optimize each specialization separately, possibly yielding better performance, and smaller size for each specialization; The whole of a GWT application can end up being many times the size of a comparable jQuery application, due to all of the precompiled specializations.
One benefit to runtime generics that no-one here has mentioned (?) is the possibility for code that is generated and injected into a running application, to use the same List, Hashmap / Dictionary etc. that everything else in that application is already using. Why you'd want to do that, is another question.
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.
My reason for asking the question:
I am using a large framework not of my own design. I need to use several "user information" classes which are unrelated as far as the code is concerned. They do not derive from any common base class, and I do not have access to the source code to recompile.
These information classes work like this: there are classes A, B, C, etc. These classes each have an information class, Ainfo, Binfo, etc. associated with them. Because the user (i.e. me) needs to attach different informations to a given object of a given class (meaning I might have two different classes deriving from Ainfo that I want to attach to an object of A), and there is only one information slot, I want to make an information object that can old other various information objects. That way, I can just add my information into this fake information-object-which-is-a-container-for-other-information-objects.
The problem arises in that I would like to do this for Ainfo, Binfo, Cinfo, Dinfo etc. So I would like to write a mixin or something that just adds the container functionality to any of the plain old info classes.
The problem is that the information classes Ainfo, Binfo, etc. require different constructor arguments.
So the question:
Is it possible to pass a vector of types into the constructor of the mixin? That way I could have a variable list of appropriate constructor parameters passed in? Can you assign a type to a variable outside of a template argument? Can you cast with this variable?
or
Is it possible to inherit from a specific object? Could I for example create a new Ainfo object using the correct constructor, then do the mixin on that specific object. This would be like the usage of the decorator pattern, except I have no common interface. (the object being decorated is the interface)
or
am I just going to have to bite the bullet and write 15,000 (exaggeration :) ) classes which are exactly the same, but inherit from a different base class and contain a different type of object?
Summary:
I need to add a container feature to several different classes while maintaining the interface of each class and utilizing their argument-taking constructors. I would like to not duplicate code.
Thanks in advance. Sorry for totally butchering terminology.
It sounds to me like what you want is a Boost.Variant. It's like a C++-style union. It is strongly typed (so that you always know what you actually stored in it), and it has a powerful visitation mechanism that makes it easy to map many different types to a single operation.
For example, you can do this:
typedef boost::variant<Ainfo, Binfo, Cinfo> CommonInfo;
//In a function.
CommonInfo someInfo = Ainfo();
You can then write visitor functors that can be used to call members of the info objects.
class DoThingInfoVisitor : boost::static_visitor<>
{
void operator()(Ainfo &info) {info.DoThing()}
void operator()(Binfo &info) {info.DoThing2()}
void operator()(Cinfo &info) {info.StepA(); info.StepB();}
};
Armed with this object, if you want to do whatever this DoThing means for any CommonInfo type:
CommonInfo someInfo = Ainfo();
boost::apply_visitor( times_two_visitor(), someInfo );
This will call the Ainfo version, since that's what happens to be stored in someInfo. If it had stored Binfo, then you could use that. You can build a suite of these visitors; they can return values, take parameters (though you'll need to store them in the functor), and various other tricks you can learn from the docs.
If it's not doable in templates, and you can't hack it with the preprocessor, then you're gonna have to do it by hand. C++ doesn't contain any type manipulation at run-time, typeid() and dynamic_cast is all you've got.
This may be a bit of an oversimplification, but if nothing else it should help to clarify your question. Using templates, you can easily generate classes that derive from your info classes. The following classes illustrate this concept.
class Ainfo {
std::string _a;
public:
void setContent(const std::string& A);
const char * print() const; // prints _a
};
class Binfo {
std::string _b;
public:
void setContent(const std::string& B);
const char * print() const; // prints _b
};
template<class Tinfo>
class Info : public Tinfo {
};
You could then use this template as follows.
Info<Ainfo> my_info;
my_info.setContent("test");
std::cout << my_info.print();
UPDATE: If you also want to override the template's constructor, try using a member template.
template<class Tinfo>
class Info : public Tinfo {
public:
template<typename arg>
Info(arg rhs) : Tinfo(rhs) { }
};
Using this, you can compile and run the following.
Info<Ainfo> my_info("Testing...");
std::cout << my_info.print();
I could be wrong, but I have a feeling that we're getting pretty close now...
I'm building an hierarchy of objects that wrap primitive types, e.g integers, booleans, floats etc, as well as container types like vectors, maps and sets. I'm trying to (be able to) build an arbitrary hierarchy of objects, and be able to set/get their values with ease. This hierarchy will be passed to another class (not mentioned here) and an interface will be created from this representation. This is the purpose of this hierarchy, to be able to create a GUI representation from these objects.To be more precise, i have something like this:
class ValObject
{
public:
virtual ~ValObject() {}
};
class Int : public ValObject
{
public:
Int(int v) : val(v) {}
void set_int(int v) { val = v);
int get_int() const { return val; }
private:
int val;
};
// other classes for floats, booleans, strings, etc
// ...
class Map : public ValObject {}
{
public:
void set_val_for_key(const string& key, ValObject* val);
ValObject* val_for_key(const string& key);
private:
map<string, ValObject*> keyvals;
};
// classes for other containers (vector and set) ...
The client, should be able to create and arbitrary hierarchy of objects, set and get their values with ease, and I, as a junior programmer, should learn how to correctly create the classes for something like this.
The main problem I'm facing is how to set/get the values through a pointer to the base class ValObject. At first, i thought i could just create lots of functions in the base class, like set_int, get_int, set_string, get_string, set_value_for_key, get_value_for_key, etc, and make them work only for the correct types. But then, i would have lots of cases where functions do nothing and just pollute my interface. My second thought was to create various proxy objects for setting and getting the various values, e.g
class ValObject
{
public:
virtual ~ValObject() {}
virtual IntProxy* create_int_proxy(); // <-- my proxy
};
class Int : public ValObject
{
public:
Int (int v) : val(v) {}
IntProxy* create_int_proxy() { return new IntProxy(&val); }
private:
int val;
};
class String : public ValObject
{
public:
String(const string& s) : val(s) {}
IntProxy* create_int_proxy() { return 0; }
private:
string val;
};
The client could then use this proxy to set and get the values of an Int through an ValObject:
ValObject *val = ... // some object
IntProxy *ipr = val->create_int_proxy();
assert(ipr); // we know that val is an Int (somehow)
ipr->set_val(17);
But with this design, i still have too many classes to declare and implement in the various subclasses. Is this the correct way to go ? Are there any alternatives ?
Thank you.
Take a look at boost::any and boost::variant for existing solutions. The closest to what you propose is boost::any, and the code is simple enough to read and understand even if you want to build your own solution for learning purposes --if you need the code, don't reinvent the wheel, use boost::any.
One of the beauties of C++ is that these kinds of intrusive solutions often aren't necessary, yet unfortunately we still see similar ones being implemented today. This is probably due to the prevalence of Java, .NET, and QT which follows these kinds of models where we have a general object base class which is inherited by almost everything.
By intrusive, what's meant is that the types being used have to be modified to work with the aggregate system (inheriting from a base object in this case). One of the problems with intrusive solutions (though sometimes appropriate) is that they require coupling these types with the system used to aggregate them: the types become dependent on the system. For PODs it is impossible to use intrusive solutions directly as we cannot change the interface of an int, e.g.: a wrapper becomes necessary. This is also true of types outside your control like the standard C++ library or boost. The result is that you end up spending a lot of time and effort manually creating wrappers to all kinds of things when such wrappers could have been easily generated in C++. It can also be very pessimistic on your code if the intrusive solution is uniformly applied even in cases where unnecessary and incurs a runtime/memory overhead.
With C++, a plethora of non-intrusive solutions are available at your fingertips, but this is especially true when we know that we can combine static polymorphism using templates with dynamic polymorphism using virtual functions. Basically we can generate these base object-derived wrappers with virtual functions on the fly only for the cases in which this solution is needed without pessimizing the cases where this isn't necessary.
As already suggested, boost::any is a great model for what you want to achieve. If you can use it directly, you should use it. If you can't (ex: if you are providing an SDK and cannot depend on third parties to have matching versions of boost), then look at the solution as a working example.
The basic idea of boost::any is to do something similar to what you are doing, only these wrappers are generated at compile-time. If you want to store an int in boost::any, the class will generate an int wrapper class which inherits from a base object that provides the virtual interface required to make any work at runtime.
The main problem I'm facing is how to
set/get the values through a pointer
to the base class ValObject. At first,
i thought i could just create lots of
functions in the base class, like
set_int, get_int, set_string,
get_string, set_value_for_key,
get_value_for_key, etc, and make them
work only for the correct types. But
then, i would have lots of cases where
functions do nothing and just pollute
my interface.
As you already correctly deduced, this would generally be an inferior design. One tell-tale sign of inheritance being used improperly is when you have a lot of base functions which are not applicable to your subclasses.
Consider the design of I/O streams. We don't have ostreams with functions like output_int, output_float, output_foo, etc. as being directly methods in ostream. Instead, we can overload operator<< to output any data type we want in a non-intrusive fashion. A similar solution can be achieved for your base type. Do you want to associate widgets with custom types (ex: custom property editor)? We can allow that:
shared_ptr<Widget> create_widget(const shared_ptr<int>& val);
shared_ptr<Widget> create_widget(const shared_ptr<float>& val);
shared_ptr<Widget> create_widget(const shared_ptr<Foo>& val);
// etc.
Do you want to serialize these objects? We can use a solution like I/O streams. If you are adapting your own solution like boost::any, it can expect such auxiliary functions to already be there with the type being stored (the virtual functions in the generated wrapper class can call create_widget(T), e.g.
If you cannot be this general, then provide some means of identifying the types being stored (a type ID, e.g.) and handle the getting/setting of various types appropriately in the client code based on this type ID. This way the client can see what's being stored and deal set/get values on it accordingly.
Anyway, it's up to you, but do consider a non-intrusive approach to this as it will generally be less problematic and a whole lot more flexible.
Use dynamic_cast to cast up the hierarchy. You don't need to provide an explicit interface for this - any reasonable C++ programmer can do that. If they can't do that, you could try enumerating the different types and creating an integral constant for each, which you can then provide a virtual function to return, and you can then static_cast up.
Finally, you could consider passing a function object, in double-dispatch style. This has a definite encapsulation advantage.
struct functor {
void operator()(Int& integral) {
...
}
void operator()(Bool& boo) {
...
}
};
template<typename Functor> void PerformOperationByFunctor(Functor func) {
if (Int* ptr = dynamic_cast<Int*>(this)) {
func(*ptr);
}
// Repeat
}
More finally, you should avoid creating types where they've basically been already covered. For example, there's little point providing a 64bit integral type and a 32bit integral type and ... it's just not worth the hassle. Same with double and float.