I have an implementation of a queue, something like template <typename T> queue<T> with a struct QueueItem { T data;} and I have a separate library that times the passage of data across different places (including from one producer thread to consumer thread via this queue). In order to do this, I inserted code from that timing library into the push and pop functions of the queue so that when they assign a BufferItem.data they also assign an extra member i added of type void* to some timing metadata from that library. I.e. what used to be something like:
void push(T t)
{
QueueItem i;
i.data = t;
//insert i into queue
}
became
void push(T t)
{
QueueItem i;
i.data = t;
void* fox = timinglib.getMetadata();
i.timingInfo = fox;
//insert i into queue
}
with QueueItem going from
struct QueueItem
{
T data;
}
to
struct QueueItem
{
T data;
void* timingInfo;
}
What I would like to achieve, however, is the ability to swap out of the latter struct in favor of the lighter weight struct whenever the timing library is not activated. Something like:
if timingLib.isInactive()
;//use the smaller struct QueueItem
else
;//use the larger struct QueueItem
as cheaply as possible. What would be a good way to do this?
You can't have a struct that is big and small at the same time, obviously, so you're going to have to look at some form of inheritance or pointer/reference, or a union.
A union would be ideal for you if there's "spare" data in T that could be occupied by your timingInfo. If not, then it's going to be as 'heavy' as the original.
Using inheritance is also likely to be as big as the original, as it'll add a vtable in there which will pad it out too much.
So, the next option is to store a pointer only, and have that point to the data you want to store, either the data or the data+timing. This kind of pattern is known as 'flyweight' - where common data is stored separately to the object that is manipulated. This might be what you're looking for (depending on what the timing info metadata is).
The other, more complex, alternative is to have 2 queues that you keep in sync. You store data in one, and the other one stores the associated timeing info, if enabled. If not enabled, you ignore the 2nd queue. The trouble with this is ensuring the 2 are kept in sync, but that's a organisational problem rather than a technical challenge. Maybe create a new Queue class that contains the 2 real queues internally.
I'll start by just confirming my assumption that this needs to be a runtime choice and you can't just build two different binaries with timing enabled/disabled. That approach eliminates as much overhead in any approach as possible.
So now let's assume we want different runtime behavior. There will need to be runtime decisions, so there are a couple options. If you can get away with the (relatively small) cost of polymorphism then you could make your queue polymorphic and create the appropriate instance once at startup and then its push for example either will or won't add the extra data.
However if that's not an option I believe you can use templates to help accomplish your end, although there will likely be some up-front work and it will probably increase the size of your binary with the extra code.
You start with a template to add timing to a class:
template <typename Timee>
struct Timed : public Timee
{
void* timingInfo;
};
Then a timed QueueItem would look like:
Timed<QueueItem> timed_item;
To anything that doesn't care about the timing, this class looks exactly like a QueueItem: It will automatically upcast or slice to the parent as appropriate. And if a method needs to know the timing information you either create an overload that knows what to do for a Timed<T> or do a runtime check (for the "is timing enabled" flag) and downcast to the correct type.
Next, you'll need to change your Queue instantiation to know whether it's using the base QueueItem or the Timed version. For example, a very very rough sketch of a possible mechanism:
template <typename Element>
void run()
{
Queue<Element> queue;
queue.setup();
queue.process();
}
int main()
{
if(do_timing)
{
run<Timed<QueueItem> >();
}
else
{
run<QueueItem>();
}
return 0;
}
You would "likely" need a specialization for Queue when used with Timed items unless getting the metadata is stateless in which case the Timed constructor can gather the info and self-populate itself when created. Then Queue just stays the same and relies on which instantiation you're using.
Related
I've been scouring the net looking for a container that handles this scenario best:
Linear memory (no gaps like an object pool or allocator would have)
Some way to give a reference to an object in container that remains persistent between adds/removals. Or a way to search quickly to find original objects.
Decently fast adds to end and removals from middle (but no inserts required)
So far the only solution I've been able to find is to use an std::vector and when a removal takes place I update all reference indices above the current index being removed. This just seems bad, looking for any other solution that would be more efficient.
Here is a horrible idea. I haven't tried it at all so there is probably more than a few bugs.
template <typename T>
class InsaneContainter {
public:
class MemberPointer {
friend class InsaneContainer;
size_t idx_;
InsaneContainter* parent_;
public:
MemberPointer(InsaneContainter* parent,size_t idx) idx_(idx),parent_(parent){}
T& operator*() {
parent->members[idx_];
}
};
friend class MemberPointer;
using Handle = std::shared_ptr<MemberPointer>;
Handle insert(const T& t) {
members.push_back(std::make_tuple(T{t},Handle{new MemberPointer{this,members.size()}));
return std::get<1>(members.back());
}
Handle GetHandle(size_t idx) {
return std::get<1>(members[idx]);
}
void delete(size_t idx) {
//swap with end
std::swap(members[idx],members.back());
std::get<1>(members[idx])->idx_=idx;
members.pop_back();
}
private:
std::vector<std::tuple<T,std::shared_ptr<MemberPointer>> members_;
};
The idea is that, at insertion time, you'll receive a handle that will always have O(1) find and delete. While it is otherwize O(n) to find the object, once you find it you can get the handle which will stay up to date.
The usage of such a structure is...limited to say the least so I suspect and X vs Y problem here.
Through lots of performance testing I found the fastest method for the general case below:
1.) Use a pool allocator that stores free memory regions.
2.) Use free memory region list to copy occupied data linearly into temporary memory at every "gap" in the pool.
This works best for me due to the nature of add/removes in my program (resulting in low fragmentation)
Would this general concept be considered "bad"? The concept of using function typedefs for precalculating which function's are better optimized to handle the data stored? Or should I just stick to if and switch statements to keep other programmers from cringing? Aside from cringing at the names in this throw together example:
#ifndef __PROJECT_CIMAGE_H_
#define __PROJECT_CIMAGE_H_
#define FORMAT_RGB 0
#define FORMAT_BGR 1
typedef unsigned char ImageFormat;
class CImage
{
protected:
// image data
Components* data;
ImageFormat format;
// typedef the functions
typedef void(*lpfnDeleteRedComponentProc)();
typedef void(*lpfnDeleteGreenComponentProc)();
typedef void(*lpfnDeleteBlueComponentProc)();
// specify the different functions for each supported format
void DeleteRedComponentRGB();
void DeleteGreenComponentRGB();
void DeleteBlueComponentRGB();
void DeleteRedComponentBGR();
void DeleteGreenComponentBGR();
void DeleteBlueComponentBGR();
// Add in references to which functions to use.
lpfnDeleteRedComponentProc DRC;
lpfnDeleteGreenComponentProc DGC;
lpfnDeleteBlueComponentProc DBC;
public:
Image(); // Allocate some basic data
~Image(); // Deallocate stored data
// change the image format
void SetImageFormat(ImageFormat format)
{
// shift through the data and adjust it as neccissary.
switch (format)
{
case FORMAT_RGB:
// use functions specially suited for the RGB format
DRC = DeleteRedComponentRGB;
DGC = DeleteGreenComponentRGB;
DBC = DeleteBlueComponentRGB;
break;
case FORMAT_BGR:
// use functions specially suited for the BGR format
DRC = DeleteRedComponentBGR;
DGC = DeleteGreenComponentBGR;
DBC = DeleteBlueComponentBGR;
break;
}
}
// Set's the specifyed component to 0 throughout the entire image
void DeleteRedComponent() { DRC(); }
void DeleteGreenComponent() { DGC(); }
void DeleteBlueComponent() { DBC(); }
// more, similarly pourposed, functions here...
};
#endif // __PROJECT_CIMAGE_H_
There are many problems with the above code.
You use #define uselessly, and typedef where you should have an enum
enum class ImageFormat:unsigned char { // unsigned char optional
FORMAT_RGB, // =0 optional
FORMAT_BGR // =1 optional
};
Second, you have a cluster of virtual behavior you want to swap out in a clump. How does this not scream interface class to you?
struct ChannelSpecific {
virtual void DeleteGreen( CImage* ) = 0;
virtual void DeleteBlue( CImage* ) = 0;
virtual void DeleteRed( CImage* ) = 0;
// etc
};
template< ImageFormat format >
struct ChannelSpecificImpl;
template<>
struct ChannelSpecificImpl<FORMAT_RGB>:ChannelSpecific {
void DeleteGreen( CImage* ) final { /* etc...*/ }
// etc...
};
template<>
struct ChannelSpecificImpl<FORMAT_BGR>:ChannelSpecific {
// etc...
};
The overhead to calling the above virtual functions is marginally higher than a function pointer (due to the vtable being less likely to be in the cache), but in cases where you are doing a whole pile of operations in a row you can find the format and explicitly cast the worker and call the final methods with no function-pointer or virtual table overhead (up to and including allowing the methods to be inlined).
As a second advantage, a whole pile of the operations you want to perform on channels ends up being exceedingly uniform, and just a matter of what the offset of each channel is. So I can do away with the two above specializations by simply doing this:
enum class Channel { Red, Green, Blue };
template<ImageFormat, Channel> struct channel_traits;
template<> struct channel_traits<FORMAT_RGB, Red>:std::integral_constant< size_t, 0 > {};
template<> struct channel_traits<FORMAT_RGB, Green>:std::integral_constant< size_t, 1 > {};
template<> struct channel_traits<FORMAT_RGB, Blue>:std::integral_constant< size_t, 2 > {};
template<> struct channel_traits<FORMAT_BGR, Red>:std::integral_constant< size_t, 2 > {};
template<> struct channel_traits<FORMAT_BGR, Green>:std::integral_constant< size_t, 1 > {};
template<> struct channel_traits<FORMAT_BGR, Blue>:std::integral_constant< size_t, 0 > {};
and now I get to write my ChannelSpecificImpl<ImageFormat> without specializations -- I just need to access the above traits classes, and I get to write my code once, and use it multiple times.
Inside CImage I store a single ChannelSpecific pointer, which holds no data, just algorithms. When I swap out the image format, the ChannelSpecific pointer is swapped out. If I find I have a bottleneck in how I'm using ChannelSpecific because of too much vtable overhead, I get to refactor and put a mega-function in it.
If I hate the fact that I'm passing in the CImage all the time, I can give ChannelSpecific state of a pointer to the CImage internally, and now the code gets to use this->cimage to access the CImage.
On the other hand, code like what you wrote above has its place. I'd consider it better than massive case switch statements.
Note that a bit of the above code is C++11 specific (enum class, enum with storage specifier, final), but if you drop those features the solution is still viable.
Also note that your switch statement ends up looking like:
switch (format) {
case FORMAT_RGB:
channelSpecific.reset(new ChannelSpecificImpl<FORMAT_RGB>());
case FORMAT_BGR:
channelSpecific.reset(new ChannelSpecificImpl<FORMAT_BGR>());
which is far less to maintain and less likely to contain bugs. If you hate the free store (and more specifically, have found that format changes are common enough that the ::new call is a performance hit that matters), create a boost::variant or a C++11 union of each of the possible ChannelSpecificImpl. (std::unique_ptr<ChannelSpecific> channelSpecific, or std::shared_ptr, depending on various things -- use unique_ptr by default.)
Finally, if you get tired of maintaining that switch statement (as I tend to), making a cascading if based magic switch via template metaprogramming isn't that hard -- or even an array of function pointer factories that produce a ChannelSpecific* and an explicit array lookup to call one of them. (sadly, there isn't a variardic template expansion that produces an actual switch statement, but compilers might optimize chained sequential ifs into the same structure anyhow).
If you get nothing from the above code, the important part is that you don't want to hand write each of the zero functions. You want to not repeat yourself, write it once, factor out the differences between the foramts into a traits class, and have template functions on the format and channel produce the function that does the work and be written once. If you don't do this, you will either have to generate your code via macros and have an undebuggable mess, generate your code via some other method (and not be able to debug the generator, just the generated code), or you will have some corner case bug that occur only when doing some specific operation to some specific channel that your QA will miss. Maybe not today, maybe not tomorrow, but someday when a change is made and someone screws up the update to the 18th format specific function but only in the blue channel.
I'm in the midst of attacking an old per-channel imaging library that was done in this "virtual C-style function pointer swap out" pretty much exactly as you are proposing, and each function I touch gets rewritten using the above technique. I am reducing the amount of code by huge swaths, increasing reliability, and sometimes even getting performance boosts. Why? Because I was able to check common assumptions -- pixelstride equal to pixel packing, pixelstride in source and dest equal -- and generate a less-branchy version for that case, and fall back to a more-branchy for the corner cases, then apply that to a whole myriad of different pixel iteration code in one fell swoop. Maintaining N different pixel iterating code with that kind of micro optimization on top of the existing micro optimizations would be expensive: doing it this way means that I get to write it once, and reap the benefits N fold.
Typedefs are a good way to make the code much more readable. If you have a problem with the typedefs then it means it just does not contribute to the code's readability. Just changing the typedef name will solve the problem but you would need to change it everywhere in the existing codebase.
#Yakk comments on using virtual instead of function pointers are on the money; as well as the better solution also offered.
Given the reservations on the design here, its worth noting that:
// typedef the functions
typedef void(*lpfnDeleteRedComponentProc)();
typedef void(*lpfnDeleteGreenComponentProc)();
typedef void(*lpfnDeleteBlueComponentProc)();
creates distinct new type names for each component, even though they have the same signature. If I were going down this path I'd have a single type name which would make clear the expected common behavior.
I am iterating over a set of callback functions. Functions are called during iteration and may lead to drastic changes to the actual container of the functions set.
What I am doing now is:
make a copy of original set
iterate over copy, but for every element check whether it still exists in the original set
Checking for every element's existence is super-dynamic, but seems quite slow too.
Are there other propositions to tackle this case?
Edit : here is the actual code :
// => i = event id
template <class Param>
void dispatchEvent(int i, Param param) {
EventReceiverSet processingNow;
const EventReceiverSet& eventReceiverSet = eventReceiverSets[i];
std::copy(eventReceiverSet.begin(), eventReceiverSet.end(), std::inserter(processingNow, processingNow.begin()));
while (!processingNow.empty()) {
EventReceiverSet::iterator it = processingNow.begin();
IFunction<>* function = it->getIFunction(); /// get function before removing iterator
processingNow.erase(it);
// is EventReceiver still valid? (may have been removed from original set)
if (eventReceiverSet.find(ERWrapper(function)) == eventReceiverSet.end()) continue; // not found
function->call(param);
}
};
Two basic approaches come to mind:
use a task based approach (with the collection locked, push tasks onto a queue for each element, then release all parties to do work and wait till completion). You'll still need a check to see whether the element for the current task is still present/current in the collection when the task is actually starting.
this could leverage reader-writer locks for the checks, which is usually speedier than fullblown mutual exclusions (especially with more readers than writers)
use a concurrent data structure (I mean, one that is suitable for multithreaded access without explicit locking). The following libraries contain implementations of concurrent data structures:
Intel Thread Building Blocks
MS ConCrt concurrent_vector
libcds Concurrent Data Structures
(adding links shortly)
There is a way to do it in two steps: first, go through the original set, and make a set of action items. Then go through the set of action items, and apply them to the original set.
An action item is a base class with subclasses. Each subclass takes in a set, and performs a specific operation on it, for example:
struct set_action {
virtual void act(std::set<int> mySet) const;
};
class del_action : public set_action {
private:
int item;
public:
del_action(int _item) : item(_item) {}
virtual void act(std::set<int> mySet) const {
// delete item from set
}
};
class upd_action : public set_action {
private:
int from, to;
public:
upd_action(int _from, int _to) : from(_from), to(_to) {}
virtual void act(std::set<int> mySet) const {
// delete [from], insert [to]
}
};
Now you can create a collection of set_action*s in the first pass, and run them in the second pass.
The operations which mutate the set structure are insert() and erase().
While iterating, consider using the iterator returned by the mutating operations.
it = myset.erase( it );
http://www.cplusplus.com/reference/stl/set/erase/
I've been stuck for some time on this problem, and I need your help.
My C++ application is running on multiple exec sites. My problem is that I cannot pass objects holding a virtual table, because sites do not share memory (thus a virtual method from a given object will lead to an undefined behaviour). By "I cannot pass" I mean : I do not want any virtual table.
The fun thing is there's not only inheritance, but also templates and eerie conception...
Here is the code
// "Main" code
List< Animals, 5 > list;
List< Animals, 8 > list2;
list.concatenate( list2 );
// GenericList.hpp
template< Type >
class GenericList
{
virtual getBuffer(void) = 0;
virtual getSize(void) = 0;
void concatenate( GenericList<Type> gList)
{
int size = gList.getSize(); // Call to the child...
...getBuffer()...
// processing, etc.
}
}
// List.hpp
template< Type, Size_ >
class List : public GenericList< Type >
{
int getSize()
{
return Size_;
}
Type * getBuffer()
{
return buffer;
}
Type buffer[Size_];
}
How can I get rid of inheritance ?
EDIT/ In light of the first few answers, I can tell you that I cannot implement a better serialization, the code being private.
If you just want to get rid of virtual tables, you don't have to get rid of inheritance. You have to get rid of virtual functions. Looking at the code you post, maybe you can make a few changes so that getSize and getBuffer are in GenericList, so you can make them non-virtual, but that really depends on the rest of your code.
The first question is, however, why would you worry about virtual tables in the first place? When you serialize the objects, you should serialize their data in order to preserve their state, and the state is the only thing you should pass around.
I think you are blaming the wrong part of the problem there... if you have a distributed system, you have to make sure that the serialized data that is sent on the wire contains enough information to rebuild the state of the object on the other end of the connection.
I believe that the problem you are facing is that you are sending raw data over the wire, while you should have a serialization mechanism that is able to encode the actual type of the object being sent and rebuild the object on the opposite end with the exact same type. In the case of an object belonging to a class with virtual functions, that will mean that the two objects are not equal bitwise, as on each end of the connection the pointer to the vtable will refer to a different location in memory, but they will be semantically equal, which is what you need to be able to process the objects on the other end.
At my workplace, we tend to use iostream, string, vector, map, and the odd algorithm or two. We haven't actually found many situations where template techniques were a best solution to a problem.
What I am looking for here are ideas, and optionally sample code that shows how you used a template technique to create a new solution to a problem that you encountered in real life.
As a bribe, expect an up vote for your answer.
General info on templates:
Templates are useful anytime you need to use the same code but operating on different data types, where the types are known at compile time. And also when you have any kind of container object.
A very common usage is for just about every type of data structure. For example: Singly linked lists, doubly linked lists, trees, tries, hashtables, ...
Another very common usage is for sorting algorithms.
One of the main advantages of using templates is that you can remove code duplication. Code duplication is one of the biggest things you should avoid when programming.
You could implement a function Max as both a macro or a template, but the template implementation would be type safe and therefore better.
And now onto the cool stuff:
Also see template metaprogramming, which is a way of pre-evaluating code at compile-time rather than at run-time. Template metaprogramming has only immutable variables, and therefore its variables cannot change. Because of this template metaprogramming can be seen as a type of functional programming.
Check out this example of template metaprogramming from Wikipedia. It shows how templates can be used to execute code at compile time. Therefore at runtime you have a pre-calculated constant.
template <int N>
struct Factorial
{
enum { value = N * Factorial<N - 1>::value };
};
template <>
struct Factorial<0>
{
enum { value = 1 };
};
// Factorial<4>::value == 24
// Factorial<0>::value == 1
void foo()
{
int x = Factorial<4>::value; // == 24
int y = Factorial<0>::value; // == 1
}
I've used a lot of template code, mostly in Boost and the STL, but I've seldom had a need to write any.
One of the exceptions, a few years ago, was in a program that manipulated Windows PE-format EXE files. The company wanted to add 64-bit support, but the ExeFile class that I'd written to handle the files only worked with 32-bit ones. The code required to manipulate the 64-bit version was essentially identical, but it needed to use a different address type (64-bit instead of 32-bit), which caused two other data structures to be different as well.
Based on the STL's use of a single template to support both std::string and std::wstring, I decided to try making ExeFile a template, with the differing data structures and the address type as parameters. There were two places where I still had to use #ifdef WIN64 lines (slightly different processing requirements), but it wasn't really difficult to do. We've got full 32- and 64-bit support in that program now, and using the template means that every modification we've done since automatically applies to both versions.
One place that I do use templates to create my own code is to implement policy classes as described by Andrei Alexandrescu in Modern C++ Design. At present I'm working on a project that includes a set of classes that interact with BEA\h\h\h Oracle's Tuxedo TP monitor.
One facility that Tuxedo provides is transactional persistant queues, so I have a class TpQueue that interacts with the queue:
class TpQueue {
public:
void enqueue(...)
void dequeue(...)
...
}
However as the queue is transactional I need to decide what transaction behaviour I want; this could be done seperately outside of the TpQueue class but I think it's more explicit and less error prone if each TpQueue instance has its own policy on transactions. So I have a set of TransactionPolicy classes such as:
class OwnTransaction {
public:
begin(...) // Suspend any open transaction and start a new one
commit(..) // Commit my transaction and resume any suspended one
abort(...)
}
class SharedTransaction {
public:
begin(...) // Join the currently active transaction or start a new one if there isn't one
...
}
And the TpQueue class gets re-written as
template <typename TXNPOLICY = SharedTransaction>
class TpQueue : public TXNPOLICY {
...
}
So inside TpQueue I can call begin(), abort(), commit() as needed but can change the behaviour based on the way I declare the instance:
TpQueue<SharedTransaction> queue1 ;
TpQueue<OwnTransaction> queue2 ;
I used templates (with the help of Boost.Fusion) to achieve type-safe integers for a hypergraph library that I was developing. I have a (hyper)edge ID and a vertex ID both of which are integers. With templates, vertex and hyperedge IDs became different types and using one when the other was expected generated a compile-time error. Saved me a lot of headache that I'd otherwise have with run-time debugging.
Here's one example from a real project. I have getter functions like this:
bool getValue(wxString key, wxString& value);
bool getValue(wxString key, int& value);
bool getValue(wxString key, double& value);
bool getValue(wxString key, bool& value);
bool getValue(wxString key, StorageGranularity& value);
bool getValue(wxString key, std::vector<wxString>& value);
And then a variant with the 'default' value. It returns the value for key if it exists, or default value if it doesn't. Template saved me from having to create 6 new functions myself.
template <typename T>
T get(wxString key, const T& defaultValue)
{
T temp;
if (getValue(key, temp))
return temp;
else
return defaultValue;
}
Templates I regulary consume are a multitude of container classes, boost smart pointers, scopeguards, a few STL algorithms.
Scenarios in which I have written templates:
custom containers
memory management, implementing type safety and CTor/DTor invocation on top of void * allocators
common implementation for overloads wiht different types, e.g.
bool ContainsNan(float * , int)
bool ContainsNan(double *, int)
which both just call a (local, hidden) helper function
template <typename T>
bool ContainsNanT<T>(T * values, int len) { ... actual code goes here } ;
Specific algorithms that are independent of the type, as long as the type has certain properties, e.g. binary serialization.
template <typename T>
void BinStream::Serialize(T & value) { ... }
// to make a type serializable, you need to implement
void SerializeElement(BinStream & strean, Foo & element);
void DeserializeElement(BinStream & stream, Foo & element)
Unlike virtual functions, templates allow more optimizations to take place.
Generally, templates allow to implement one concept or algorithm for a multitude of types, and have the differences resolved already at compile time.
We use COM and accept a pointer to an object that can either implement another interface directly or via [IServiceProvider](http://msdn.microsoft.com/en-us/library/cc678965(VS.85).aspx) this prompted me to create this helper cast-like function.
// Get interface either via QueryInterface of via QueryService
template <class IFace>
CComPtr<IFace> GetIFace(IUnknown* unk)
{
CComQIPtr<IFace> ret = unk; // Try QueryInterface
if (ret == NULL) { // Fallback to QueryService
if(CComQIPtr<IServiceProvider> ser = unk)
ser->QueryService(__uuidof(IFace), __uuidof(IFace), (void**)&ret);
}
return ret;
}
I use templates to specify function object types. I often write code that takes a function object as an argument -- a function to integrate, a function to optimize, etc. -- and I find templates more convenient than inheritance. So my code receiving a function object -- such as an integrator or optimizer -- has a template parameter to specify the kind of function object it operates on.
The obvious reasons (like preventing code-duplication by operating on different data types) aside, there is this really cool pattern that's called policy based design. I have asked a question about policies vs strategies.
Now, what's so nifty about this feature. Consider you are writing an interface for others to use. You know that your interface will be used, because it is a module in its own domain. But you don't know yet how people are going to use it. Policy-based design strengthens your code for future reuse; it makes you independent of data types a particular implementation relies on. The code is just "slurped in". :-)
Traits are per se a wonderful idea. They can attach particular behaviour, data and typedata to a model. Traits allow complete parameterization of all of these three fields. And the best of it, it's a very good way to make code reusable.
I once saw the following code:
void doSomethingGeneric1(SomeClass * c, SomeClass & d)
{
// three lines of code
callFunctionGeneric1(c) ;
// three lines of code
}
repeated ten times:
void doSomethingGeneric2(SomeClass * c, SomeClass & d)
void doSomethingGeneric3(SomeClass * c, SomeClass & d)
void doSomethingGeneric4(SomeClass * c, SomeClass & d)
// Etc
Each function having the same 6 lines of code copy/pasted, and each time calling another function callFunctionGenericX with the same number suffix.
There were no way to refactor the whole thing altogether. So I kept the refactoring local.
I changed the code this way (from memory):
template<typename T>
void doSomethingGenericAnything(SomeClass * c, SomeClass & d, T t)
{
// three lines of code
t(c) ;
// three lines of code
}
And modified the existing code with:
void doSomethingGeneric1(SomeClass * c, SomeClass & d)
{
doSomethingGenericAnything(c, d, callFunctionGeneric1) ;
}
void doSomethingGeneric2(SomeClass * c, SomeClass & d)
{
doSomethingGenericAnything(c, d, callFunctionGeneric2) ;
}
Etc.
This is somewhat highjacking the template thing, but in the end, I guess it's better than play with typedefed function pointers or using macros.
I personally have used the Curiously Recurring Template Pattern as a means of enforcing some form of top-down design and bottom-up implementation. An example would be a specification for a generic handler where certain requirements on both form and interface are enforced on derived types at compile time. It looks something like this:
template <class Derived>
struct handler_base : Derived {
void pre_call() {
// do any universal pre_call handling here
static_cast<Derived *>(this)->pre_call();
};
void post_call(typename Derived::result_type & result) {
static_cast<Derived *>(this)->post_call(result);
// do any universal post_call handling here
};
typename Derived::result_type
operator() (typename Derived::arg_pack const & args) {
pre_call();
typename Derived::result_type temp = static_cast<Derived *>(this)->eval(args);
post_call(temp);
return temp;
};
};
Something like this can be used then to make sure your handlers derive from this template and enforce top-down design and then allow for bottom-up customization:
struct my_handler : handler_base<my_handler> {
typedef int result_type; // required to compile
typedef tuple<int, int> arg_pack; // required to compile
void pre_call(); // required to compile
void post_call(int &); // required to compile
int eval(arg_pack const &); // required to compile
};
This then allows you to have generic polymorphic functions that deal with only handler_base<> derived types:
template <class T, class Arg0, class Arg1>
typename T::result_type
invoke(handler_base<T> & handler, Arg0 const & arg0, Arg1 const & arg1) {
return handler(make_tuple(arg0, arg1));
};
It's already been mentioned that you can use templates as policy classes to do something. I use this a lot.
I also use them, with the help of property maps (see boost site for more information on this), in order to access data in a generic way. This gives the opportunity to change the way you store data, without ever having to change the way you retrieve it.