I need to implement a container to hold an amount of elements and for some reason, it has to work without any heap allocation. Another requirement is, that the container elements should not be copied or moved in any way. They have to constructed directly into the memory allocated by the container.
For that, I decided to use placement new and delegate the memory management completely to the container implementation (found some useful information about placement new at drdobbs).
A running example is found here.
(Please note, that the use of new uint8_t[size] and std::queue is just to keep the example simple. My real code has more complex, heap-less implementation instead.)
This perfectly works so far, as the client code has to put elements into the container with calls like:
executer.push(new (executer) MyRunnable("Hello", 123));
Now I want do remove the need of the repeated write executer in this statement. I would rather like to write something like e.g.:
executer.pushNew(MyRunnable("Hello", 123));
or
executer.pushNew(MyRunnable, "Hello", 123);
maybe by providing an appropriate template but I failed to write one (no preprocessor macros, please).
I'd found some useful information about std::allocator here at drdobbs but don't know how to apply it to my problem (further, the article is of anno 2000 and so don't take use of possible C++11 advantages).
Could one help me to find a way to not longer need to give the executer twice?
Edit: After successful approving Jarod42's answer, I'd updated my running example code here.
And for the history, here the original example code of my initial question:
#include <iostream>
#include <queue>
class Runnable {
// Runnable should be uncopyable and also unmovable
Runnable(const Runnable&) = delete;
Runnable& operator = (const Runnable&) = delete;
Runnable(const Runnable&&) = delete;
Runnable& operator = (const Runnable&&) = delete;
public:
explicit Runnable() {}
virtual ~Runnable() {}
virtual void run() = 0;
};
class MyRunnable: public Runnable {
public:
explicit MyRunnable(const char* name, int num): name(name), num(num) {}
virtual void run() override {
std::cout << name << " " << num << std::endl;
}
private:
const char* name;
int num;
};
class Executer {
// Executer should be uncopyable and also unmovable
Executer(const Executer&) = delete;
Executer& operator = (const Executer&) = delete;
Executer(const Executer&&) = delete;
Executer& operator = (const Executer&&) = delete;
public:
explicit Executer() {
}
void* allocateEntry(size_t size) {
// this heap allocation is just to keep this example simple
// my real implementation uses it's own memory management instead (blockpool)
return new uint8_t[size];
}
void push(Runnable* entry) {
queue.push(entry);
}
template <typename R> // this don't works
void pushNew(R) {
push(new (*this) R);
}
inline friend void* operator new(size_t n, Executer& executer) {
return executer.allocateEntry(n);
}
void execute() {
while (queue.size() > 0) {
Runnable* entry = queue.front();
queue.pop();
entry->run();
// Now doing "placement delete"
entry->~Runnable();
uint8_t* p = reinterpret_cast<uint8_t*>(entry);
delete[] p;
}
}
private:
// this use of std::queue is just to keep this example simple
// my real implementation uses it's own heap-less queue instead
std::queue<Runnable*> queue {};
};
int main() {
Executer executer;
executer.push(new (executer) MyRunnable("First", 1));
executer.push(new (executer) MyRunnable("Second", 2));
executer.push(new (executer) MyRunnable("Third", 3));
// but want to use it more like one this
//executer.pushNew(MyRunnable("Fifth", 5)); // how to implement it?
//executer.pushNew(MyRunnable, "Sixth", 6); // or maybe for this usage?
executer.execute();
}
There are two things wrong with this:
template <typename R> // this don't works
void pushNew(R) {
push(new (*this) R);
}
The first is answered by Jarod42 in that you want to do:
template <typename R, typename... Ts>
void pushNew(Ts&&... args) {
push(new (*this) R(std::forward<Ts>(args)...));
}
but even more importantly... new (*this) R is really bizarre. It looks like you're constructing an R over yourself! But you're not, you're just using that syntax to call your allocator. That horribly violates the principle of least surprise. It took me quite a while to understand what was going on.
What you should to is just use your allocator directly:
template <typename R, typename... Ts>
void pushNew(Ts&&... args) {
void* slot = allocateEntry(sizeof(R));
push(new (slot) R(std::forward<Ts>(args)...));
}
That is a lot easier to understand.
With:
template <typename R, typename... Ts>
void pushNew(Ts&&... args) {
push(new (*this) R(std::forward<Ts>(args)...));
}
You can write:
executor.PushNew<MyRunnable>("Hello", 123);
instead of
executer.push(new (executer) MyRunnable("Hello", 123));
Related
This question is based on the example code below, which is inspired by Sean Parent's talk.
The goal of the code below is to provide an object wrapper similar to boost::any. I wrote this code to educate myself of type erasure. So, there is no practical uses this code intends (considering there is already boost::any).
class ObjWrap {
public:
template <typename T>
ObjWrap(T O) : Self(new Obj<T>(std::move(O))) {}
template <typename T>
friend typename T * getObjPtr(ObjWrap O) {
return static_cast<T*>(O.Self->getObjPtr_());
}
private:
struct Concept {
virtual ~Concept() = 0;
virtual void* getObjPtr_() = 0;
};
template <typename T>
struct Obj : Concept {
Obj(T O) : Data(std::move(O)) {}
void* getObjPtr_() { return static_cast<void*>(&Data); }
T Data;
};
std::unique_ptr<Concept> Self;
};
Before I can really ask my question, let's examine the code in the following aspects:
Concept::getObjPtr_ returns void* because a) Concept cannot be a template otherwise unique_ptr<Concept> Self would not work; b) void* is the only way I know how to return Obj::Data in a type-agnostic way in C++. Please correct me if this is wrong...
T * getObjPtr(ObjWrap O) is a template that needs instantiation separately from the ObjWrap constructor.
The use of ObjWrap basically includes: a) make a new ObjWrap over an existing object; b) retrieve the underlying object given an ObjWrap. For example:
ObjWrap a(1);
ObjWrap b(std::string("b"));
int* p_a = getObjPtr<int>(a);
std::string* p_b = getObjPtr<std::string>(b);
This works but it is obvious that getObjPtr<int>(b) does not work as intended.
So, my question is:
Is there a way to fix the above code so that we can simply use int* p_a = getObjPtr(a) and std::string* p_b = getObjPtr(b) or better yet auto p_a = getObjPtr(a) and auto p_b = getObjPtr(b)? In other words, is there a way in C++ to instantiate two templates at the same time (if so, we can instantiate the ObjWrap constructor and T* getObjPtr(ObjWrap) at compile time of a ObjWrap object, e.g., ObjWrap a(1))?
Edit 1:
Making ObjWrap a templated class does not help since it defeats the purpose of type erasure.
template <typename T>
class ObjWrap {
/* ... */
};
ObjWrap<int> a(1); // this is no good for type erasure.
Edit 2:
I was reading the code and realize that it can be modified to reflect the idea a little better. So, please also look at the following code:
class ObjWrap {
public:
template <typename T>
ObjWrap(T O) : Self(new Obj<T>(std::move(O))) {}
template <typename T>
T * getObjPtr() {
return static_cast<T*>(Self->getObjPtr_());
}
private:
struct Concept {
virtual ~Concept() = 0;
virtual void* getObjPtr_() = 0;
};
template <typename T>
struct Obj : Concept {
Obj(T O) : Data(std::move(O)) {}
void* getObjPtr_() { return static_cast<void*>(&Data); }
T Data;
};
std::unique_ptr<Concept> Self;
};
int main() {
ObjWrap a(1);
ObjWrap b(std::string("b"));
int* p_a = a.getObjPtr<int>();
std::string* p_b = b.getObjPtr<std::string>();
std::cout << *p_a << " " << *p_b << "\n";
return 0;
}
The main difference between this version of the code versus the one above is that T * getObjPtr() is a member function that is encapsulated by the ObjWrap object.
Edit 3:
My question regarding type erasure is answered by accepted answer. However, the question on simultaneous type instantiation to multiple templates is yet to be answered. My guess is currently C++ does not allow it but it would be nice to hear from people with more experience on that.
There are a few things that may help.
First thing to say is that if Obj ever needs to expose the address of the object, it's not Sean Parent's 'inheritance is the root of all evil' type-erasing container.
The trick is to ensure that the interface of Obj offers all semantic actions and queries the wrapper will ever need.
In order to provide this, it's often a reasonable idea to cache the address of the object and its type_id in the concept.
Consider the following updated example, in which there is one public method - operator==. The rule is that two Objs are equal if they contain the same type of object and those objects compare equal.
Note that the address and type_id:
1) are implementation details and not exposed on the interface of Obj
2) are accessible without virtual calls, which short-circuits the not-equal case.
#include <memory>
#include <utility>
#include <typeinfo>
#include <utility>
#include <cassert>
#include <iostream>
class ObjWrap
{
public:
template <typename T>
ObjWrap(T O) : Self(new Model<T>(std::move(O))) {}
// objects are equal if they contain the same type of model
// and the models compare equal
bool operator==(ObjWrap const& other) const
{
// note the short-circuit when the types are not the same
// this means is_equal can guarantee that the address can be cast
// without a further check
return Self->info == other.Self->info
&& Self->is_equal(other.Self->addr);
}
bool operator!=(ObjWrap const& other) const
{
return !(*this == other);
}
friend std::ostream& operator<<(std::ostream& os, ObjWrap const& o)
{
return o.Self->emit(os);
}
private:
struct Concept
{
// cache the address and type here in the concept.
void* addr;
std::type_info const& info;
Concept(void* address, std::type_info const& info)
: addr(address)
, info(info)
{}
virtual ~Concept() = default;
// this is the concept's interface
virtual bool is_equal(void const* other_address) const = 0;
virtual std::ostream& emit(std::ostream& os) const = 0;
};
template <typename T>
struct Model : Concept
{
Model(T O)
: Concept(std::addressof(Data), typeid(T))
, Data(std::move(O)) {}
// no need to check the pointer before casting it.
// Obj takes care of that
/// #pre other_address is a valid pointer to a T
bool is_equal(void const* other_address) const override
{
return Data == *(static_cast<T const*>(other_address));
}
std::ostream& emit(std::ostream& os) const override
{
return os << Data;
}
T Data;
};
std::unique_ptr<Concept> Self;
};
int main()
{
auto x = ObjWrap(std::string("foo"));
auto y = ObjWrap(std::string("foo"));
auto z = ObjWrap(int(2));
assert(x == y);
assert(y != z);
std::cout << x << " " << y << " " << z << std::endl;
}
http://coliru.stacked-crooked.com/a/dcece2a824a42948
(etc. etc.) Please correct me if this is wrong...
Your premise is wrong at least in principle, if not also in practice. You're insisting on making getObjPtr() a virtual method, and using an abstract base class. But - you've not established this is necessary. Remember - using virtual methods is expensive! Why should I pay for virtuals just to get type erasure?
Is there a way to fix the above code so that we can simply use int* p_a = getObjPtr(a)
Take Sean Parent's talk title to heart (as opposed to the fact that he does use inheritance in the talk), drop the inheritance and the answer should be Yes. Edit: It's sufficient for the code that erases the type and the code that un-erases the type to know what the type is - as long as you don't need to act on the type-erased data in a type-specific way. In Sean Parent's talk, you need to be able to make non-trivial copies of it, to move it, to draw it etc. With std::any/boost::any you might need copying and moving, which may require virtuals - but that's the most general use case.
Even std::any limits what you can and can't do, as is discussed in this question:
why doesn't std::any_cast support implicit conversion?
I need to implement a container to hold an amount of elements and for some reason, it has to work without any heap allocation. Another requirement is, that the container elements should not be copied or moved in any way. They have to constructed directly into the memory allocated by the container.
For that, I decided to use placement new and delegate the memory management completely to the container implementation (found some useful information about placement new at drdobbs).
A running example is found here.
(Please note, that the use of new uint8_t[size] and std::queue is just to keep the example simple. My real code has more complex, heap-less implementation instead.)
This perfectly works so far, as the client code has to put elements into the container with calls like:
executer.push(new (executer) MyRunnable("Hello", 123));
Now I want do remove the need of the repeated write executer in this statement. I would rather like to write something like e.g.:
executer.pushNew(MyRunnable("Hello", 123));
or
executer.pushNew(MyRunnable, "Hello", 123);
maybe by providing an appropriate template but I failed to write one (no preprocessor macros, please).
I'd found some useful information about std::allocator here at drdobbs but don't know how to apply it to my problem (further, the article is of anno 2000 and so don't take use of possible C++11 advantages).
Could one help me to find a way to not longer need to give the executer twice?
Edit: After successful approving Jarod42's answer, I'd updated my running example code here.
And for the history, here the original example code of my initial question:
#include <iostream>
#include <queue>
class Runnable {
// Runnable should be uncopyable and also unmovable
Runnable(const Runnable&) = delete;
Runnable& operator = (const Runnable&) = delete;
Runnable(const Runnable&&) = delete;
Runnable& operator = (const Runnable&&) = delete;
public:
explicit Runnable() {}
virtual ~Runnable() {}
virtual void run() = 0;
};
class MyRunnable: public Runnable {
public:
explicit MyRunnable(const char* name, int num): name(name), num(num) {}
virtual void run() override {
std::cout << name << " " << num << std::endl;
}
private:
const char* name;
int num;
};
class Executer {
// Executer should be uncopyable and also unmovable
Executer(const Executer&) = delete;
Executer& operator = (const Executer&) = delete;
Executer(const Executer&&) = delete;
Executer& operator = (const Executer&&) = delete;
public:
explicit Executer() {
}
void* allocateEntry(size_t size) {
// this heap allocation is just to keep this example simple
// my real implementation uses it's own memory management instead (blockpool)
return new uint8_t[size];
}
void push(Runnable* entry) {
queue.push(entry);
}
template <typename R> // this don't works
void pushNew(R) {
push(new (*this) R);
}
inline friend void* operator new(size_t n, Executer& executer) {
return executer.allocateEntry(n);
}
void execute() {
while (queue.size() > 0) {
Runnable* entry = queue.front();
queue.pop();
entry->run();
// Now doing "placement delete"
entry->~Runnable();
uint8_t* p = reinterpret_cast<uint8_t*>(entry);
delete[] p;
}
}
private:
// this use of std::queue is just to keep this example simple
// my real implementation uses it's own heap-less queue instead
std::queue<Runnable*> queue {};
};
int main() {
Executer executer;
executer.push(new (executer) MyRunnable("First", 1));
executer.push(new (executer) MyRunnable("Second", 2));
executer.push(new (executer) MyRunnable("Third", 3));
// but want to use it more like one this
//executer.pushNew(MyRunnable("Fifth", 5)); // how to implement it?
//executer.pushNew(MyRunnable, "Sixth", 6); // or maybe for this usage?
executer.execute();
}
There are two things wrong with this:
template <typename R> // this don't works
void pushNew(R) {
push(new (*this) R);
}
The first is answered by Jarod42 in that you want to do:
template <typename R, typename... Ts>
void pushNew(Ts&&... args) {
push(new (*this) R(std::forward<Ts>(args)...));
}
but even more importantly... new (*this) R is really bizarre. It looks like you're constructing an R over yourself! But you're not, you're just using that syntax to call your allocator. That horribly violates the principle of least surprise. It took me quite a while to understand what was going on.
What you should to is just use your allocator directly:
template <typename R, typename... Ts>
void pushNew(Ts&&... args) {
void* slot = allocateEntry(sizeof(R));
push(new (slot) R(std::forward<Ts>(args)...));
}
That is a lot easier to understand.
With:
template <typename R, typename... Ts>
void pushNew(Ts&&... args) {
push(new (*this) R(std::forward<Ts>(args)...));
}
You can write:
executor.PushNew<MyRunnable>("Hello", 123);
instead of
executer.push(new (executer) MyRunnable("Hello", 123));
I recently found out that the .* operator (and the closely related ->* operator) exists in C++. (See this question.)
Seems neat at first, but why would I ever need such a thing? The two answers in the linked question provided contrived examples which would benefit from a direct function call.
Where a direct function call is inconvenient, a function object could be used instead, like the lambda functions that may be used in std::sort. This removes a level of indirection and hence would be more performant than using .*.
The linked question also mentioned a simplified version of this example:
struct A {
int a;
int b;
};
void set_member(A& obj, int A::* ptr, int val){
obj.*ptr = val;
}
int main()
{
A obj;
set_member(obj, &A::b, 5);
set_member(obj, &A::a, 7);
// Both members of obj are now assigned
}
But it's pretty trivial (perhaps even better practice because it's cleaner and isn't unnecessarily constrained to members of A) to do this instead:
struct A {
int a;
int b;
};
void set_me(int& out, int val){
out = val;
}
int main()
{
A obj;
set_me(obj.b, 5);
set_me(obj.a, 7);
// Both members of obj are now assigned
}
In conclusion, a pointer-to-member-function might be replaced by a function object, and a pointer-to-member-variable might be replaced by a direct reference of said variable or a function object. Doing so might also increase the efficiency of the code due to one less indirection.
This question only provides examples where my conclusion stands, so it does not answer my question.
Apart from interfacing legacy code which uses .* (in which there would be no choice at all), when, really, would I want to use .*?
Your example is too trivial to be illustrative. Consider a bit more complicated one
struct A {
int a;
int b;
};
void set_n_members(A objs[], unsigned n, int A::* ptr, int val)
{
for (unsigned i = 0; i < n; ++i)
objs[i].*ptr = val;
}
int main()
{
A objs[100];
set_n_members(objs, 100, &A::b, 5);
set_n_members(objs, 100, &A::a, 7);
}
How would you rewrite this without int A::* ptr and without inducing code bloat?
You could create collections of pointers to members and iterate over them. E.g.:
struct UserStrings
{
std::string first_name;
std::string surname;
std::string preferred_name;
std::string address;
};
...
std::array<std::string UserStrings::*, 4> str_cols = { &UserStrings::first_name, &UserStrings::surname, &UserStrings::preferred_name, &UserStrings::address };
std::vector<UserStrings> users = GetUserStrings();
for (auto& user : users)
{
for (auto& column : str_cols)
{
SanitizeForSQLQuery(user.*column);
}
}
It is used to implement std::mem_fn, which is used to implement std::function.
The following code shows how the ->* operator works in a naive Function class implemention.
Similarly, you can implement a member invoker class using the .* operator and a class reference.
#include <iostream>
class A
{
public:
void greet()
{
std::cout << "Hello world"<<std::endl;
}
};
template<typename R, typename ...TArgs>
class Invoker
{
public:
virtual R apply(TArgs&& ...args) = 0;
};
template<typename C, typename R, typename ...TArgs>
class MemberInvoker :public Invoker<R, TArgs...>
{
protected:
C* sender;
R(C::*function)(TArgs ...args);
public:
MemberInvoker(C* _sender, R(C::*_function)(TArgs ...args))
:sender(_sender)
, function(_function)
{
}
virtual R apply(TArgs&& ...args) override
{
return (sender->*function)(std::forward<TArgs>(args)...);
}
};
template<typename T>
class Func
{
};
template<typename R, typename ...TArgs>
class Func<R(TArgs...)>
{
public:
Invoker<R,TArgs...>* invoker=nullptr;
template<typename C>
Func(C* sender, R(C::*function)(TArgs...))
{
invoker =new MemberInvoker<C, R, TArgs...>(sender, function);
}
R operator()(TArgs&& ...args)
{
return invoker->apply(std::forward<TArgs>(args)...);
}
~Func()
{
if (invoker)
{
delete invoker;
invoker = nullptr;
}
}
};
int main()
{
A a;
Func<void()> greetFunc(&a, &A::greet);
greetFunc();
system("PAUSE");
}
Let's say you wanted to write a LINQ style library for C++ that could be used something like this:
struct Person
{
std::string first_name;
std::string last_name;
std::string occupation;
int age;
int children;
};
std::vector<Person> people = loadPeople();
std::vector<std::string> result = from(people)
.where(&Person::last_name == "Smith")
.where(&Person::age > 30)
.select("%s %s",&Person::first_name,&Person::last_name);
for(std::string person : result) { ... };
Under the covers, the where function accepts an expression tree containing a pointer to member (among other stuff) and is applied to each vector item looking for one that matches. The select statement accepts a format string and some pointer to members and does an sprintf style formatting of whichever vector items get through the where statements.
I have written something like this, and there are several others out there that do it slightly differently (Is there a LINQ library for C++?). Pointer-to-member allows the library user to specify whichever members of their struct that they want and the library doesn't need to know anything about what they might do.
I want to add delegates to my game engine. I'm used to them in c# and now I can't live without them.
I have seen several implementations here and on external sources but they are not multicast. The delegate is just allowed to store one listener at a time. Do anybody know of any good implementation in memory/performance terms with += and -= support? I have tried writting mine using variadic templates but I'm not there yet.
Edit: This is what I have come with until now:
template<typename ... Args>
class Delegate
{
public:
Delegate() =default;
~Delegate() = default;
template<typename U>
Delegate& operator += (const U &func)
{
_listeners.push_back(std::function<void(Args...)>(func));
return *this;
}
template<typename Class, typename Method>
Delegate& operator += (const std::function<void(Args...)> func)
{
_listeners.push_back(func);
return *this;
}
void operator() (Args... params)
{
for (auto listener : _listeners)
{
listener(params...);
}
}
private:
std::list<std::function<void(Args...)>> _listeners;
};
It is working really well. Here you can see how to use it:
Delegate<std::string> del;
del += std::bind(&GameScene::print, this, std::placeholders::_1);
del += [](const std::string& str) { log(str.c_str()); };
del("text");
I know I will use the std::bind version almost all the time because this is used in a event messaging system and instances will subscribe to the delegate using a class method (by class method I am not refering to static method but a method of the class). Is there anyway to improve the std::bind part? I think it is a bit ugly and annoying when you have to add some funcions with placeholders, etc...
Cheers.
Basically, a naive C++ 11 implementation could look like:
#include <vector>
#include <functional>
class Foo
{
public:
std::vector< std::function< void() > > onSomething;
};
int main( void )
{
Foo f;
f.onSomething.push_back( [&] { /* Do something... */ } );
f.onSomething.push_back( [&] { /* Do something else... */ } );
return 0;
}
Note the use of std::function, which allows to store lambdas in a vector.
You'll then be able to iterate through the vector, and execute each lambda.
Then, of course, operator overloading and thread safety if needed... But this should be trivial.
Suppose I have an autolocker class which looks something like this:
template <T>
class autolocker {
public:
autolocker(T *l) : lock(l) {
lock->lock();
}
~autolocker() {
lock->unlock();
}
private:
autolocker(const autolocker&);
autolocker& operator=(const autolocker&);
private:
T *lock;
};
Obviously the goal is to be able to use this autolocker with anything that has a lock/unlock method without resorting to virtual functions.
Currently, it's simple enough to use like this:
autolocker<some_lock_t> lock(&my_lock); // my_lock is of type "some_lock_t"
but it is illegal to do:
autolocker lock(&my_lock); // this would be ideal
Is there anyway to get template type deduction to play nice with this (keep in my autolocker is non-copyable). Or is it just easiest to just specify the type?
Yes you can use the scope-guard technique
struct autolocker_base {
autolocker_base() { }
protected:
// ensure users can't copy-as it
autolocker_base(autolocker_base const&)
{ }
autolocker_base &operator=(autolocker_base const&)
{ return *this; }
};
template <T>
class autolocker : public autolocker_base {
public:
autolocker(T *l) : lock(l) {
lock->lock();
}
autolocker(const autolocker& o)
:autolocker_base(o), lock(o.lock)
{ o.lock = 0; }
~autolocker() {
if(lock)
lock->unlock();
}
private:
autolocker& operator=(const autolocker&);
private:
mutable T *lock;
};
Then write a function creating the autolocker
template<typename T>
autolocker<T> makelocker(T *l) {
return autolocker<T>(l);
}
typedef autolocker_base const& autolocker_t;
You can then write it like this:
autolocker_t lock = makelocker(&my_lock);
Once the const reference goes out of scope, the destructor is called. It doesn't need to be virtual. At least GCC optimizes this quite well.
Sadly, this means you have to make your locker-object copyable since you need to return it from the maker function. But the old object won't try to unlock twice, because its pointer is set to 0 when it's copied, so it's safe.
Obviously you can't get away with autolocker being a template, because you want to use it as a type, and templates must be instantiated in order to obtain types.
But type-erasure might be used to do what you want. You turn the class template into a class and its constructor into a member template. But then you'd have to dynamically allocate an inner implementation object.
Better, store a pointer to a function that performs the unlock and let that function be an instance of a template chosen by the templatized constructor. Something along these lines:
// Comeau compiles this, but I haven't tested it.
class autolocker {
public:
template< typename T >
autolocker(T *l) : lock_(l), unlock_(&unlock<T>) { l->lock(); }
~autolocker() { unlock_(lock_); }
private:
autolocker(const autolocker&);
autolocker& operator=(const autolocker&);
private:
typedef void (*unlocker_func_)(void*);
void *lock_;
unlocker_func_ unlock_;
template <typename T>
static void unlock(void* lock) { ((T*)lock)->unlock(); }
};
I haven't actually tried this and the syntax might be wrong (I'm not sure how to take the address of a specific function template instance), but I think this should be doable in principle. Maybe someone comes along and fixes what I got wrong.
I like this a lot more than the scope guard, which, for some reason, I never really liked at all.
I think jwismar is correct and what you want is not possible with C++. However, a similar (not direct analogue) construct is possible with C++0x, using several new features (rvalues/moving and auto variable type):
#include <iostream>
template <typename T>
class autolocker_impl
{
public:
autolocker_impl(T *l) : lock(l) {
lock->lock();
}
autolocker_impl (autolocker_impl&& that)
: lock (that.lock)
{
that.lock = 0;
}
~autolocker_impl() {
if (lock)
lock->unlock();
}
private:
autolocker_impl(const autolocker_impl&);
autolocker_impl& operator=(const autolocker_impl&);
private:
T *lock;
};
template <typename T>
autolocker_impl <T>
autolocker (T* lock)
{
return autolocker_impl <T> (lock);
}
struct lock_type
{
void lock ()
{ std::cout << "locked\n"; }
void unlock ()
{ std::cout << "unlocked\n"; }
};
int
main ()
{
lock_type l;
auto x = autolocker (&l);
}
autolocker is a class template, not a class. Your "this would be ideal" is showing something that doesn't make sense in C++.