Reputation: 4594
I've been writing a memory manager in C++ and have been using a macro to replace the traditional call to new
. The impetus behind this is two-fold: I need to capture type information about the requested allocation and I want to produce clean syntax. A tall task for a macro, sure. But here is what I've come up with:
#define anew( TYPE ) new ( Allocator::Instance( )->Allocate<TYPE>( ) ) TYPE
It calls the allocator to make a spot in the memory pool for an object of type TYPE
(with respect to the macro). It then uses the pointer returned by Allocate
as the parameter for placement new
, allowing for the constructor to be run on this freshly "allocated" piece of memory. Makes sense thus far. So in normal usage, this is how it expands:
Obj* a = anew( Obj )( );
Obj* a = new ( Allocator::Instance( )->Allocate<Obj>( ) ) Obj( );
And this works great! But when I try to use it with a templated class that has more than one parameter, it explodes (by which I mean it generates an error).
Obj<int, bool>* a = anew( Obj<int, bool> )( );
This complains about there not being enough type parameters supplied to the template arguments. After some poking, I understand that it's because of the comma in the template parameters. That being the case, how do I fix this?
Better yet, is there a better way to do this? I feel like it makes sense to make this a macro, because I really just want some direct-text replacement to occur here. But would I gain anything from approaching this problem differently? Is it possible to accomplish this without using macros? I've tried this route and the issue that arises is calling the constructor. The following code is not valid, although it would be nice:
Obj* a = anew<Obj>( );
where the parenthesis can only correspond to valid constructors on the given type.
Any help would be greatly appreciated. Thanks!
[EDIT]
Although Variadic Macros solved my problem as well, the answer I selected below is what I believe to be the best C++ solution to my issue. Thank you all for solving this so quickly.
Upvotes: 2
Views: 56
Reputation: 64308
With C++11, you could do this:
template <typename T,typename... Args>
T* anew(Args&&... args)
{
return new(Allocator::Instance()->Allocate<T>()) T(std::forward<Args>(args)...);
}
and now you can use this form:
Obj* a = anew<Obj>( );
and pass anything (or nothing) for the parameters to the constructor.
std::forward
is the general mechanism for allowing parameters to be forwarded from one function to another. It relies on tricks of rvalue references and reference collapsing with templates to make it where lvalues and rvalues are both passed correctly and efficiently. Combined with variadics, this allows any number of parameters to be perfectly forwarded this way.
Upvotes: 7