Sign in to follow this  
Trapper Zoid

C++ friendly solution to C style type casting?

Recommended Posts

Trapper Zoid    1370
I have a C++ programming problem that I've solved using C style casts but I fear it's an ugly hack. I was wondering if there was a better way to solve this problem rather than delving into the black arts of type casting. I have implemented a signal/slot system that I will use at the core of my latest game engine. As part of this system I have implemented a static memory pool to deal with storing the links between signals and the slot functions they need to call. The slot functions themselves are stored using Don Clugston's Fast Delegate code. The signals overload the brackets operator so they can act like functions but pass the arguments to many slots. The problem arises because I support a variety of argument types. Templates deal with differing types but for different numbers of arguments I have to use different classes. There is a Fast Delegate class for each number of arguments you want to use in your functions (FastDelegate0, FastDelegate1 etc.) However this means my memory pool; which is basically a big array at its core; needs to handle a variety of different FastDelegate classes. Each one of those FastDelegate classes is exactly the same size in memory, so from a hardware perspective it should not be a problem. However C++ does not like mixing types so wantonly. My present solution is to implement a storage class that takes up the right amount of memory but does not actually do anything:
union DelegateMemory
{
    char _f0[sizeof(fastdelegate::FastDelegate0<>)];
    char _f1[sizeof(fastdelegate::FastDelegate1<int>)];
    char _f2[sizeof(fastdelegate::FastDelegate2<int, int>)];
    char _f3[sizeof(fastdelegate::FastDelegate3<int, int, int>)];
    char _f4[sizeof(fastdelegate::FastDelegate4<int, int, int, int>)];
    char _f5[sizeof(fastdelegate::FastDelegate5<int, int, int, int, int>)];
    char _f6[sizeof(fastdelegate::FastDelegate6<int, int, int, int, int, int>)];
    char _f7[sizeof(fastdelegate::FastDelegate7<int, int, int, int, int, int, int>)];
    char _f8[sizeof(fastdelegate::FastDelegate8<int, int, int, int, int, int, int, int>)];
};
I didn't really need to declare so many in the union since they should be all the same size, but it helps underline that it could be any FastDelegate (and not just ones that take ints either). Then I use a nasty cast to convert between the right FastDelegate type and DelegateMemory for storage, then back again when I need the original FastDelegate type:
/**
    nasty_cast - designed to swap one block of memory to another
    Done to subvert the C++ type casting system
    This is a horrible hack, but it makes the global memory buffer happen

    Some ideas were taken from Don Clugston's casts in his Fast Delegate code
    for how to do the casts and type checking
*/
template <class OutputClass, class InputClass>
inline OutputClass nasty_cast(InputClass input){
    DGL_COMPILE_ASSERT(sizeof(InputClass)==sizeof(OutputClass));
    return *(OutputClass *)(&(input));
}
Example castings:
newConnection->functionDelegate = nasty_cast<DelegateMemory>(MyDelegateType(object, memberFunction));
(nasty_cast<MyDelegateType>(slotInfo->functionDelegate))();
However I don't know if there's a better way I could get around this problem without going back to C for a type cast solution. I suppose I should be happy that it works in this form but does anyone have a solution that doesn't trample all over the C++ type system?

Share this post


Link to post
Share on other sites
ToohrVyk    1596
If you want to convert different types to a single type, and then back to the original type, the ideal approach is to make the single type a base class of all other types, static_cast from the different types to the single type, and dynamic_cast from the single type back to what you believe is the original type (the result being that you don't need to remember the correct type).


// Convert to an unique common base type
FastDelegate1<int> d;
Base& b = static_cast<Base&>(d);

// Works
FastDelegate1<int> & back = dynamic_cast<FastDelegate<int>&>(b);

// Throws a bad_cast exception
FastDelegate1<float> & back = dynamic_cast<FastDelegate<float>&>(b);







Another advantage is a single modification of the class template (to inherit from the base class) is enough. You don't have to alter some object or another every single time you need a new set of arguments, as the template instantiation does everything necessary.

And now, for the actual problem



I spotted, inside your code, the words "memory pool". Why does a memory pool care about what its memory will be used for? The idiom in C++ is to overload operator new and operator delete to work in conjunction with the memory pool. The pool itself should only store the ununitialized (important emphasis) blocks used for storage, with no knowledge of what kind of object will use that memory when it leaves the pool.

I'm tired and it's late, but the following should be an example of pool allocation, using boost::pool to handle the memory (any void* allocating class will do):

boost::pool<> BaseClass :: staticPool (sizeof(BaseClass));

void* BaseClass :: operator new (size_t size) {
boost::pool<> & p = BaseClass :: staticPool;
assert (size < p.get_requested_size());
return p.malloc();
}

void BaseClass :: operator delete(void* chunk, size_t size) {
boost::pool<> & p = BaseClass :: staticPool;
assert (size < p.get_requested_size());
p.free(chunk);
}


Then, you can use new and delete on your objects to create them using pool memory.

[Edited by - ToohrVyk on May 4, 2007 6:20:04 PM]

Share this post


Link to post
Share on other sites
Trapper Zoid    1370
Quote:
Original post by Alpha_ProgDes
I take it some (twisted) form of polymorphism and varargs wouldn't do the trick?

I was originally thinking this wasn't possible without hacking through the Fast Delegate code, but now I've had another look through the library it looks like there might be the hooks in there for a polymorphism approach to work. I'm not sure about whether using varargs will make things nastier.

The way I've currently implemented my signals there's also a seperate class for each number of arguments, for example:
Signal2<int, bool> mySignal

From the perspective of all the code using signals I think this is fine, but it does mean the signal code itself involves a lot of cut and paste, which gets annoying if I have to make any changes.

Share this post


Link to post
Share on other sites
Trapper Zoid    1370
Quote:
Original post by ToohrVyk

And now, for the actual problem



I spotted, inside your code, the words "memory pool". Why does a memory pool care about what its memory will be used for? The idiom in C++ is to overload operator new and operator delete to work in conjunction with the memory pool. The pool itself should only store the ununitialized (important emphasis) blocks used for storage, with no knowledge of what kind of object will use that memory when it leaves the pool.

That might just be a better solution than what I'm currently doing. I hadn't considered overloading new and delete (mainly because I'm still not that comfortable with C++ over C, and overloading the memory allocator is a litte bit scary!). I also didn't know about Boost's pool code; currently I've rolled my own using an array (std::vector) of a structure. I'll read up about Boost.Pool to see what it can do as I'll need more pools later on in the library.

Share this post


Link to post
Share on other sites
Ravyne    14300
What about borrowing some tricks from Expression Templates? They seem to handle these multiple template arguements quite handily, using type information to deducethe correct template(s).

Share this post


Link to post
Share on other sites
raptorstrike    181
It seem to me that for your union you could just do something like this instead

#include <iostream>

void DoNothing(){std::cout << "hi\n";};

typedef void(*pfunc)();

template<class Type>
class DelegateMemory
{
public:
char Memory[sizeof(Type)];
operator Type * (void) {return (Type*)(Memory);}
const int size() {return sizeof(Memory);}
};

int main()
{
DelegateMemory<pfunc> New;
*New = DoNothing;

(*New)();

system("pause");

};


This is just an example I whipped up and I'm not sure it makes the casting situation any better but it does allow for any type to be stored in a char array and then be spit out as that same type again, however, I have not tested it extensively but it seems to work and, assuming I understood what you were asking for correctly, should work for you too. Of course I could be way off base but I hope I helped

[smile]

Share this post


Link to post
Share on other sites
Trapper Zoid    1370
Thanks for the replies everyone.

ravyne2001:
I don't know what an expression template is, so I'll have to look them up to see what they can do. If I could get away with having just one definition for each of the functions rather than making a copy for each number of variables that would be great, otherwise I'll stick to have a specialised type for each one.

raptorstrike:
I think that might work, but I'm not too fussed about the union. I defined it that way to make absolutely sure that every one of those FastDelegate elements was exactly the same size, because if they weren't my compile time asserts would fail in the tests.

Share this post


Link to post
Share on other sites
ajones    432
I'm in a bit of a hurry, so sorry if I haven't applied enough thought to this and my suggestion isn't very helpful after all...

Have you looked at possible boost::variant applications (with an appropriate visitor) here?

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this