I want to implement a Mesh class for a CG project, but have run into some problems.
What I want to do is a Mesh class that hides implementation details (like loading to a specific API: OpenGL, DirectX, CUDA, ...) from the user. Additionally, since the Mesh class will be used in research projects, this Mesh class has to be very flexible.
class Channel {
virtual loadToAPI() = 0;
}
template <class T>
class TypedChannel : public Channel {
std::vector<T> data;
};
template <class T>
class OpenGLChannel : public TypedChannel<T> {
loadToAPI(); // implementation
};
class Mesh {
template<class T>
virtual TypedChannel<T>* createChannel() = 0; // error: no virtual template functions
std::vector<Channel*> channels;
};
class OpenGLMesh {
template<class T>
TypedChannel<T>* createChannel()
{
TypedChannel<T>* newChannel = new OpenGLChannel<T>;
channels.push_back(newChannel);
return newChannel;
};
};
For flexibility, each Mesh is really a collection of channels, like one position channel, a normal channel, etc. that describe some aspects of the mesh. A channel is a wrapper around a std::vector with some added functionality.
To hide implementation details, there is a derived class for each API (OpenGLMesh, DirectXMesh, CUDAMesh, ...) that handles API-specific code. The same goes for the Channels (OpenGLChannel, etc. that handle loading of the Channel data to the API). The Mesh acts as a factory for the Channel objects.
But here is the problem: Since the Channels are template classes, createChannel must be a template method, and template methods cannot be virtual. What I would need is something like a Factory Pattern for creating templated objects. Does anyone have advice on how something similar could be accomplished?
Thanks
It's an interesting problem, but let's discuss the compiler error first.
As the compiler said, a function cannot be both virtual and template. To understand why, just think about the implementation: most of the times, objects with virtual functions have a virtual table, which stores a pointer to each function.
For templates however, there are as many functions as combinations of type: so what should be the virtual table like ? It's impossible to tell at compilation time, and the memory layout of your class includes the virtual table and has to be decided at compilation time.
Now on to your problem.
The simplest solution would be to just write one virtual method per type, of course it can soon become tedious, so let's pretend you haven't heard that.
If Mesh is not supposed to know about the various types, then surely you don't need the function to be virtual, because who would know, given an instance of Mesh, with which type invoking the function ?
Mesh* mesh = ...;
mesh.createChannel<int>(); // has it been defined for that `Mesh` ??
On the other hand, I will suppose that OpenGLMesh does know exactly which kind of TypedChannel it will need. If so, we could use a very simple trick.
struct ChannelFactory
{
virtual ~ChannelFactory() {}
virtual Channel* createChannel() = 0;
};
template <class T>
struct TypedChannelFactory: ChannelFactory
{
};
And then:
class Mesh
{
public:
template <class T>
Channel* addChannel()
{
factories_type::const_iterator it = mFactories.find(typeid(T).name());
assert(it != mFactories.end() && "Ooops!!!" && typeid(T).name());
Channel* channel = it->second->createChannel();
mChannels.push_back(channel);
return channel;
} // addChannel
protected:
template <class T>
void registerChannelFactory(TypedChannelFactory<T>* factory)
{
mFactories.insert(std::make_pair(typeid(T).name(), factory));
} // registerChannelFactory
private:
typedef std::map < const char*, ChannelFactory* const > factories_type;
factories_type mFactories;
std::vector<Channel*> mChannels;
}; // class Mesh
It demonstrates a quite powerful idiom known as type erasure. You probably used it even before you knew the name :)
Now, you can define OpenGLMesh as:
template <class T>
struct OpenGLChannelFactory: TypedChannelFactory<T>
{
virtual Channel* createChannel() { return new OpenGLChannel<T>(); }
};
OpenGLMesh::OpenGLMesh()
{
this->registerChannelFactory(new OpenGLChannelFactory<int>());
this->registerChannelFactory(new OpenGLChannelFactory<float>());
}
And you'll use it like:
OpenGLMesh openGLMesh;
Mesh& mesh = openGLMesh;
mesh.addChannel<int>(); // fine
mesh.addChannel<float>(); // fine
mesh.addChannel<char>(); // ERROR: fire the assert... (or throw, or do nothing...)
Hope I understood what you needed :p
If you could extract factory from Mesh (introducing some ChannelFactory), then you can use templatized factory:
template <class T>
class ChannelFactory
{
public:
virtual TypedChannel<T>* createChannel() = 0;
};
Than you could derive your OpenGLMesh from ChannelFactory, , , whatever.
The only limitation of this approach is that you should know beforehand which template parameters you want use in OpenGLMesh.
Otherwise you could be interested how Boost.Any works (boost::any holds values of arbitrary type).
I'd say your whole design is broken.
virtual TypedChannel<T>* createChannel() = 0; // error: no virtual template functions
std::vector<Channel*> channels;
These two lines just don't make any sense together. Don't try to fix compiler error, think over your concepts.
To start, what exactly is your reasoning for making CreateChannel a virtual member?
To put it another way, C++ is a language known for allowing all kinds of tangled unintelligible designs. And you managed to design something that even C++ thinks is too twisted.
By channel do you mean 'spacial index'?
If you want to hide implementation details, why do you have them right in your mesh?
You want the mesh to be the same basic format, maybe templating on float, double or morton numbers in different cases. It's not the mesh that should change, just the way it gets loaded.
Related
Let say I have the following code:
class Block{
private:
data Data;
public:
data getData();
Block(arg3 Arg3, arg4 Arg4);
};
Actually, there are several ways to build a block, but always with the same member Data and method getData(), the only difference is how to build the block. In other words, the only difference is the constructor...
Instead of writing a different class for each building process, I could factorize parts of my code, defining and declaring getData in an abstract class, if there were such thing as a virtual constructor in c++ that I could write differently for each derived class corresponding to a different building process.
I do not have a lot experience for this kind of things, so I wondered if there was an alternative to a virtual constructor ? or may be a different way to do this factorization ?
PS: I am aware of https://isocpp.org/wiki/faq/virtual-functions#virtual-ctors but it seems quite complex regarding what I want to do, which seems quite common... I just want to factorize shared code between several classes, which corresponds to everything except the constructor. And I want to force new classes corresponding to other building processes to implement a new constructor.
More details about my particular situation:
I have an algorithm where I use blocks and it does not depend on their building process, so I have implemented the algorithm using a template argument to represent a block indifferently of its building process. But I use a few methods and its constructor, so I need my classes representing blocks to all have the same kind of methods I need and the same constructor to use them as a template argument of my algorithm implementation. That is why I thought of abstract class, to force a newly implemented class representing blocks to have the methods and the constructor I need in the algorithm I implemented. May be it is a bad design pattern and that is why I am stuck...
EDIT
Thank you for your answers so far. I tried to be a little generic but I feel that it is actually too vague, even with the details I gave at the end. So here is what I thought to do: I have a Matrix class as follows
// Matrix.hpp
template<typename GenericBlock> class Matrix{
std::vector<GenericBlock> blocks;
Matrix(arg1 Arg1, arg2 Arg2);
};
template<typename GenericBlock>
Matrix<GenericBlock>::Matrix(arg1 Arg1, arg2 Arg2){
// Do stuff
GenericBlock B(arg3 Arg3, arg4 Arg4);
B.getData();
}
The blocks are actually compressed, and there exists several ways to compress them and it does not change anything in the class Matrix. To avoid writing a matrix class for each compression technics, I used a template argument as you saw. So I just need to write a class for each compression technics, but they must have the same methods and constructor arguments to be compatible with Matrix.
That is why I thought of doing an abstract class, for writing a class for each compression technics. In the abstract class, I would write everything needed in Matrix so that every derived class would be compatible with Matrix. My problem now in my example is: I can define getData in the abstract class because it is always the same (for example, Datacan be the number of rows). The only thing derived classes would really need to define is the constructor.
One solution would be to not have an abstract class and use a protected constructor may be. But it does not force newly derived class to reimplement a constructor. That is why I am stuck. But I think this problem is generic enough to interest other people. So is there an alternative to a virtual constructor in this case ? (may be a factory pattern but it seems quite complex for a such common problem) If not, is there a better way to implement a matrix class whose blocks can be built in different manners, i.e. whose constructor can be different from each other, while having the same data and a few method in common ?
PS: I am interested in compression technics that produce low rank matrices, that is why the data is always the same, but not the building process.
From what you have shared so far it is not clear why you need an abstract class or virtual constructors. A factory function for each way of building a block will do:
class Block {
Data data;
public:
Block(Data d) : data(std::move(d)) {}
Data getData();
};
Block createABlock() { return Block{Data{1.0, 2.0, 3.0}}; }
Block createBBlock() { return Block{Data{42.0, 3.14, 11.6}}; }
int main() {
auto b1 = createABlock();
auto b2 = createBBlock();
}
Live demo.
Perhaps this needs to be extended with an abstract factory so you can pass a generic block factory around:
using BlockFactory = std::function<Block()>;
int main() {
BlockFactory f = createABlock;
auto b3 = f();
}
EDIT:
Regarding your EDIT, what you have suggested works fine. You don't need a virtual constructor. The template type GenericBlock just has to satisfy the implicit interface defined by the template. It doesn't need to derive from a particular base class (although it could do). The only thing required of it, is it must have a constructor that takes a particular set of arguments and a getData method. What you have is compile time static polymorphism, virtual functions are for run time dynamic polymorphism.
Inheritance will work fine but as I said above I'd be tempted to use some sort of factory. You may not need to template the whole Matrix class as only the constructor needs the factory. If the factory is known at compile-time this can be passed as a template parameter:
class Matrix {
std::vector<Block> blocks;
public:
template<typename BlockFactory>
Matrix(BlockFactory f);
};
template<typename BlockFactory>
Matrix::Matrix(BlockFactory f){
// Do stuff...
Block B = f();
auto data = B.getData();
for (auto v : data)
std::cout << v << " ";
std::cout << "\n";
}
int main() {
Matrix ma(createABlock);
Matrix mb(createBBlock);
}
Live demo.
TL:DR, but if the data are the same for all Blocks, you don't even need multiple classes, but only multiple constructors.
class Block
{
enum { type1, type2, type3 };
int type;
data Data;
public:
Block(int x)
: type(type1), Data(x) {}
Block(std::string const& str)
: type(type2), Data(str) {}
Block(data const*x)
: type(type3), Data(data) {}
/* ... */
};
template<class T>struct tag_t{constexpr tag_t(){}; usong type=T;};
template<class T>constexpr tag_t<T> tag{};
this lets you pass types as values.
struct BlockA{};
struct BlockB{};
class Block {
enum BlockType { typeA, typeB };;
BlockType type;
data Data;
public:
Block(tag_t<BlockA>, int x)
: type(typeA), Data(x) {}
Block(tag_t<BlockB>, int x)
: type(typeB), Data(2*x+7) {}
/* ... */
};
the blocks are all the same type. The tag determines how they are constructed.
There is no alternative to a virtual constructor, because there is no virtual constructor to begin with. I know it can be hard to accept, but it is the truth.
Anyhow, you do not need anything like what a virtual constructor would be if it existed....
[..] the only difference is how to build the block. In other words, the
only difference is the constructor...
If the only difference is in the constructor, then simply make the constructor take a parameter that tells what type of block is required. Alternatively you can have some functions that construct blocks in different ways:
struct Block {
private:
Block(){}
friend Block createWoodenBlock();
friend Block createStoneBlock();
};
Block createWoodenBlock(){ return Block(); }
Block createStoneBlock(){ return Block(); }
int main() {
Block woody = createWoodenBlock();
Block stony = createStoneBlock();
}
I have a base class representing an item with some common properties (name, a few flags, etc):
class AbstractItem;
class MacroDefinition : public AbstractItem;
I also have a templatized class which manages collections of these items, also taking care of common functionality like loading them from XML files on disk:
template <class ItemT>
class AbstractItemManager
{
public:
AbstractItemManager();
ItemT* GetAt(int index);
vector<ItemT*> Get(...);
private:
vector<ItemT*> mItems;
};
For any given type of AbstractItem, I can create a manager class of that appropriate type, have the base functionality handled for me, and then layer functionality specific to that type on top of that:
class MacroManager : public AbstractItemManager<MacroDefinition>
{
public:
MacroManager():AbstractItemManager<MacroDefinition>();
};
The fact that the manager class takes the type of item as a template parameter means I can make calls like this, both within MacroManager and externally, and get items of the appropriate type without having to blindly cast pointers all over the place.
MacroManager* macroManager = new MacroManager();
Macro* macro = macroManager->GetAt(2);
Now I'm implementing another class. I want to be able to pass it a reference to an AbstractItemManager so that I can access the list of items in any given manager class. However, I need to make the compiler understand that ItemT will always be derived from AbstractItem. I'd like to be able to do something like this:
class FavoriteAbstractItemList
{
public:
FavoriteAbstractItemList(AbstractItemManager* manager)
:mManager(manager)
{
vector<AbstractItem*> items = mManager->Get(...);
...
}
private:
AbstractItemManager* mManager;
};
Consequently:
FavoriteAbstractItemList* list = new FavoriteAbstractItemList(macroManager);
Of course, this is invalid, because I'm not supplying a template argument to AbstractItemManager when I'm using it in FavoriteAbstractItemList. Because my manager subclasses (MacroManager etc.) have all different ItemT types, I'm stuck here.
I imagine that I could change my class hierarchy a bit, and this would work:
template<class ItemT>
class AbstractItemManager_Base;
class AbstractItemManager : public AbstractItemManager_Base<AbstractItem>;
class MacroManager : public AbstractItemManager;
But then the template argument ItemT would be set in stone as AbstractItem in MacroManager etc., so I'd have to explicitly cast all items within MacroManager to Macro and take care to ensure that only items of type Macro were be added to it.
This seems like it's probably a common problem, but not one that has a straightforward answer. I don't have too much hands-on experience with C++ templates, so I'd greatly appreciate being set straight on this issue. Given the tradeoffs I've presented, what's the most sensible way to accomplish what I'm looking for? Or am I approaching things the wrong way to begin with?
Thanks for all your helpful answers. I ended up going with the solution that you both proposed. It hadn't occurred to me that I could use a template type to override an already-defined base type, but the compile-time chicanery of C++ templates is something I'm slowly getting used to.
As for the vector problem, that's unfortunate, but I ended up going with one of the proposed solutions and creating a separate method in the templatized class that calls the original method and stuffs everything into a new vector<ItemT*> with a bunch of static casts. I'm sure that adds a little bit of overhead, but it's still far more elegant than my knee-jerk solution of abandoning templates entirely. The only thing I really lose is the ability to directly iterate over mItems in subclasses without a cast from AbstractItem* to Macro* (etc.), but I can certainly deal with that.
Here's the new class hierarchy, in essence:
class AbstractItemManager
{
public:
virtual AbstractItem* GetAt(int index);
vector<AbstractItem*> Get(...);
protected:
vector<AbstractItem*> mItems;
};
template <class ItemT>
class TemplatizedItemManager : public AbstractItemManager
{
public:
virtual ItemT* GetAt(int index);
std::vector<ItemT*> GetItems(...);
};
class MacroManager : public TemplatizedItemManager<Macro>;
Thanks again!
class AbstractItemManager_Base
{
public:
virtual AbstractItem* GetAt (int index) = 0;
};
template <class ItemT>
class AbstractItemManager : public AbstractItemManager_Base
{
ItemT* GetAt (int index); // works if ItemT derives from AbstractItem
};
Now you can use an AbstractItemManager_Base in FavoriteAbstractItemList.
Replacing vector<ItemT*> Get(...) is somewhat more involved. vector<AbstractItem*> is not compatible with vector<ItemT*>, for any ItemT. You can try to create your own container hierarchy, such that myvector<AbstractItem*> is somehow compatible with myvector<ItemT*>; or provide an iterator-based interface to your ItemManager so that it is a container; or just have two separate unrelated functions, one returning vector<ItemT*> and the other returning vector<AbstractItem*>.
You actually have two problems. The first one is aboutz GetAt. This has a simple solution: Don't template the base, template the derived:
class AbstractItem
{
// ...
};
class MacroDefintiion:
public AbstractItem
{
// ...
};
class AbstractItemMananger
{
public:
virtual AbstractItem* GetAt(int) = 0;
// ...
};
template<typename Item> class SpecificAbstractItemManager
{
public:
Item* GetAt(); // covariant return type
// ...
};
class MacroManager: public SpecificAbstractItemManager
{
// ...
};
The second one is your Get method. That one is problematic because std::vector<Derived*> and std::vector<Base*> are unrelated classes, as far as C++ is concerned, and therefore you cannot use them for covariant return types.
Probably the best solution here is to have two functions in the derived class, one returning a std::vector<AbstractItem> (inherited from and overriding the base class function) and another one returning an std::vector<Item*>.
That is, in AbstractItemManager you have
std::vector<AbstractItem*> Get() = 0;
and in SpecificAbstractItemManager<Item> you have e.g.
std::vector<AbstractItem*> Get() { return GetSpecific(); }
std::vector<Item*> GetSpecific();
Mixins and function templates are two different ways of providing a behavior to a wide set of types, as long as these types meet some requirements.
For example, let's assume that I want to write some code that allows me to save an object to a file, as long as this object provides a toString member function (this is a rather silly example, but bear with me). A first solution is to write a function template like the following:
template <typename T>
void toFile(T const & obj, std::string const & filename)
{
std::ofstream file(filename);
file << obj.toString() << '\n';
}
...
SomeClass o1;
toFile(o1, "foo.txt");
SomeOtherType o2;
toFile(o2, "bar.txt");
Another solution is to use a mixin, using CRTP:
template <typename Derived>
struct ToFile
{
void toFile(std::string const & filename) const
{
Derived * that = static_cast<Derived const *>(this);
std::ofstream file(filename);
file << that->toString() << '\n';
}
};
struct SomeClass : public ToFile<SomeClass>
{
void toString() const {...}
};
...
SomeClass o1;
o.toFile("foo.txt");
SomeOtherType o2;
o2.toFile("bar.txt");
What are the pros and cons of these two approaches? Is there a favored one, and if so, why?
The first approach is much more flexible, as it can be made to work with any type that provides any way to be converted to a std::string (this can be achieved using traits-classes) without the need to modify that type. Your second approach would always require modification of a type in order to add functionality.
Pro function templates: the coupling is looser. You don't need to derive from anything to get the functionality in a new class; in your example, you only implement the toString method and that's it. You can even use a limited form of duck typing, since the type of toString isn't specified.
Pro mixins: nothing, strictly; your requirement is for something that works with unrelated classes and mixins cause them to be become related.
Edit: Alright, due to the way the C++ type system works, the mixin solution will strictly produce unrelated classes. I'd go with the template function solution, though.
I would like to propose an alternative, often forgotten because it is a mix of duck-typing and interfaces, and very few languages propose this feat (note: very close to Go's take to interfaces actually).
// 1. Ask for a free function to exist:
void toString(std::string& buffer, SomeClass const& sc);
// 2. Create an interface that exposes this function
class ToString {
public:
virtual ~ToString() {}
virtual void toString(std::string& buffer) const = 0;
}; // class ToString
// 3. Create an adapter class (bit of magic)
template <typename T>
class ToStringT final: public ToString {
public:
ToStringT(T const& t): t(t) {}
virtual void toString(std::string& buffer) const override {
toString(buffer, t);
}
private:
T t; // note: for reference you need a reference wrapper
// I won't delve into this right now, suffice to say
// it's feasible and only require one template overload
// of toString.
}; // class ToStringT
// 4. Create an adapter maker
template <typename T>
ToStringT<T> toString(T const& t) { return std::move(ToStringT<T>(t)); }
And now ? Enjoy!
void print(ToString const& ts); // aka: the most important const
int main() {
SomeClass sc;
print(toString(sc));
};
The two stages is a bit heavyweight, however it gives an astonishing degree of functionality:
No hard-wiring data / interface (thanks to duck-typing)
Low-coupling (thanks to abstract classes)
And also easy integration:
You can write an "adapter" for an already existing interface, and migrate from an OO code base to a more agile one
You can write an "interface" for an already existing set of overloads, and migrate from a Generic code base to a more clustered one
Apart from the amount of boiler-plate, it's really amazing how you seamlessly pick advantages from both worlds.
A few thoughts I had while writing this question:
Arguments in favor of template functions:
A function can be overloaded, so third-party and built-in types can be handled.
Arguments in favor of mixins:
Homogeneous syntax: the added behavior is invoked like any other member functions. However, it is well known that the interface of a C++ class includes not only its public member functions but also the free functions that operates on instances of this type, so this is just an aesthetic improvement.
By adding a non-template base class to the mixins, we obtain an interface (in the Java/C# sense) that can be use to handle all objects providing the behavior. For example, if we make ToFile<T> inherits from FileWritable (declaring a pure virtual toFile member function), we can have a collection of FileWritable without having to resort to complicated heterogeneous data structures.
Regarding usage, I'd say that function templates are more idiomatic in C++.
I have a class template ResourceManager and it is intended to be used something like this:
ResourceManager<Image>* rm =
ResourceManager<Image>::Instance();
Image* img = rm->acquire("picture.jpg");
rm->release(img);
I'd like to use dependency injection (pass the ResourceManager as a parameter to functions that are supposed to use it instead of having it used globally), however given that it's a template I don't know how to do this. Do you have any suggestions?
My game is only at the beginning of the development and I already have four resource types (Image, Font, Animation and Sound) so making a single ResourceManager (i.e. not a template) with an acquire function for each type of resource is not an option.
Edit: Some clarifications.
What I'm looking for is not how to do dependency injection for one type of ResourceManager, but for all of them at once.
My GameState objects need to load resources when they are initialized/opened; they do so through the ResourceManagers. However, GameStates may need to load any number of types of resources: animations, fonts, images, sounds, etc — that's a lot of function parameters for each kind of ResourceManager! What do you suggest I do?
Well, if the function needs a particular kind of resource (most will, probably), just define the specific template instance as parameter:
function(ResourceManager<Image> *rm, ...);
If the function needs any kind of resource than it can either
Be a template itself, like:
template <typename T>
function(ResourceManager<T> *rm, ...);
It will probably need to refer to the resource obtained from resource manager, so it will need the template argument in more places anyway.
Use a polymorphic base class. That would mean you'd have to define something like
class ResourceManagerBase { /* methods you need to call via the base class */ };
template <typename T>
class ResourceManager : ResourceManagerBase { ... };
function(ResourceManagerBase *rm, ...)
The function can call any methods defined in the base class. If the methods depend on resource-type internally, they will be declared abstract virtual (virtual returnType method(...) = 0) in the base and defined in the template class itself. You can also use dynamic_cast to check which particular instantiation of ResourceManager you've got.
Note, that if the function needs to refer to the resource, you will similarly need a ResourceBase abstract base class of all resources, so you can refer to any kind of resource.
The choice is matter of trade-off between faster but very large code with template function (the function will be compiled for each specialization separately) or slower but smaller code with virtual methods (call to virtual method is slower, but there is no code duplication). Also the template variant will compile slower, because most compilers will generate the code for each object file that uses it and than merge identical copies at link time.
First. Remember when you use templates, you must resolve everything at compile time.
Then ResourceManager in your code seems a singleton. So there's no difference, rougly speaking, with a global variable.
And I think that is useless pass rm as parameters of your functions when you can call the singleton directly.
And this resolves, I hope, your question.
An example using constructor injection:
template<typename T>
struct ResourceManager
{
virtual T* acquire(std::string const& resourceName)
{ return ...; }
... etc ...
};
class ImageUser
{
ResourceManager<Image>* rm_;
public:
explicit ImageUser(ResourceManager<Image>* rm)
: rm_(rm)
{}
ImageUser()
: rm_(ResourceManager<Image>::Instance())
{}
void UseImage()
{
Image* img = rm_->acquire("picture.jpg");
rm_->release(img);
}
};
struct ImageResourceManagerFake : ResourceManager<Image>
{
virtual Image* acquire(std::string const& resourceName) // override
{ return <whatever-you-want>; }
... etc ...
};
void test_imageuser()
{
ImageResourceManagerFake* rm = new ImageResourceManagerFake;
ImageUser iu(rm);
... test away ...
}
Note that I've left out all resource management; use smart pointers etc wherever applicable.
Even after your edit I find it a little hard to understand what you're after without seeing the whole picture, but I'll make a second attempt at a basic example:
template<typename T>
struct ResourceManager
{
virtual T* acquire(std::string const& resourceName)
{ return ...; }
// ... etc ...
};
class ImageUser
{
ResourceManager<Image>* rm_;
public:
explicit ImageUser(ResourceManager<Image>* rm)
: rm_(rm)
{}
void UseImage()
{
Image* img = rm_->acquire("picture.jpg");
rm_->release(img);
}
};
template<typename T>
struct ResourceManagerFake : ResourceManager<T>
{
T* acquireRetVal;
virtual T* acquire(std::string const& resourceName)
{ return acquireRetVal; }
// ... etc ...
};
void test_imageuser()
{
ResourceManagerFake<Image>* rm = new ResourceManagerFake<Image>;
rm->acquireRetVal = new Image;
ImageUser iu(rm);
iu.UseImage();
}
Note to commenters: I'm well aware of the resource leaks, but that's not the point here.
I'm pretty sure the answer is "you can't use templates, you have to use virtual functions (dynamic polymorphism)", but it seems like I'd have to duplicate a lot of code if I went that route. Here is the setup:
I currently have two classes, ColorImageSegmentation and GrayscaleImageSegmentation. They do essentially the same thing, but there are three differences
- they operate on different types (ColorImage and GrayscaleImage)
- a parameter, the dimensionality of the histogram (3 vs 1) is different
- The PixelDifference function is different based on the image type
If I create a class
template <TImageType>
class ImageSegmentation
{
};
I would be in good shape. However, I want to have this object as a member of another class:
class MyMainClass
{
ImageSegmentation MyImageSegmentation;
};
But the user needs to determine the type of MyImageSegmentation (if the user opens a grayscale image, I want to instantiate MyImageSegmentation<GrayScaleType>. Likewise for a color image, MyImageSegmentation<ColorType>.)
With derived classes, I could store a pointer and then do:
class MyMainClass
{
ImageSegmentation* MyImageSegmentation;
};
... user does something...
MyImageSegmentation = new ColorImageSegmentation;
but how would I do something like this with templates? The problem is I have a lot of:
typedef TImageType::HistogramType HistogramType;
typedef TImageType::PixelType PixelType;
sort of things going on, so I don't know how I would convert them to the dynamic polymorphic model without duplicating a whole bunch of code.
Sorry for the rambling... does anyone have any suggestions for me?
Thanks,
David
Maybe there are additional requirements you haven't told us about, but from what you have so far, you can pass the type down through the containing class:
template<typename TImage>
class MyMainClass
{
ImageSegmentation<TImage> MyImageSegmentation;
};
Most likely you'll need some layer of dynamic dispatch, but only at the highest level of abstraction:
struct IMainClass
{
virtual bool SaveToFile(std::string filename) = 0;
virtual bool ApplySharpenFilter(int level) = 0;
...
};
template<typename TImage>
class MyMainClass : public IMainClass
{
ImageSegmentation<TImage> MyImageSegmentation;
public:
virtual bool SaveToFile(std::string filename);
virtual bool ApplySharpenFilter(int level);
};
IMainClass* pMain = new MyMainClass<GrayscaleImage>();
You want to create a templated version of your objects but have those objects take different parameter types based on the templated parameter? That's not a very easy thing to integrate into a library but there are a few ways of going about it.
Take a look at unary_function for inspiration. There they are using templated traits to carry around the type parameters without having to work any sort of magic:
template <class Arg, class Result>
struct unary_function {
typedef Arg argument_type;
typedef Result result_type;
};
'unary_function' does not contain any functionality other than declaring typedefs. These typedefs, however, allow you to express in code and at compile time named equivalents between code segments. They leverage the way template parameters are checked.
What this means is that you can have objects that work on this:
template<typename T>
struct Foo{
typedef typename T::argument_type argument_type;
Foo(T _myFunc) : m_Func(_myFunc)
void myWrappedFunction(argument_type _argument){ m_Func( _argument ); }
};
which contains within it the value type of the arguments without having to specify them in advance. So if you have pixel_type or something similar for each of your image objects then simply stating typename T::pixel_type will call forward the type parameter you need.