How to implement decorators in C and C++
I have a situation in C as well as in C++ which can be best solved with something like the Python like decorators: I have few a functions which I'd like to wrap around with something else so that before the function enters some statements are performs and when it leaves some other functionality is executed.
For instance, I have a few functions in a library C file which when called should lock a semaphore and before returning the control to callee, should release the semaphore. without the lock they have following structure:
int f1(int)
{
...
...
}
int f2(char*)
{
....
}
int f3(blabla)
{
....
}
... fn(...)
I'd like to define a global semaphore which should be locked before each of these functions is called and released when the function is returned. I'd like to do it with as much simplicity as possible; something close to this:
#lockprotected
int f1(int)
{
... /* nothing changed over here */
}
#endlockprotected
or something like
int f1(int)
{
... /* nothing changed over here */
}
#lockp开发者_高级运维rotected f1
What I don't want is:
- Change the function names as they are library functions and are being called from many places.
- Explicitly place any statement before return calls as most of the functions have many early returns in between. Or for that matter change any internals of the function.
What would be the most elegant way?
Use RAII (resource acquisition is initialization) to define the lock on the mutex. This would allow you to forget about point #2, i.e., you don't need to keep track of return statement to release the lock.
class Lock {
public:
Lock () { // acquire the semaphore }
~Lock () { // release the semaphore }
}
Next create objects of this class at the start of your functions, i.e.,
int f1 (int) {
Lock l;
// you can now forget about release of this lock
// as ~Lock() will take care of it
}
A side advantage of this is that even in the case of exceptions being thrown from f1()
, you still don't need to worry about releasing the lock. All stack objects are destroyed before a function exits.
if you really want a C solution, you could go with macros like:
#define LOCK lock( &yourglobalsemaphore )
#define UNLOCK unlock( &yourglobalsemaphore )
#define LOCKED_FUNCTION_ARG1(TRet, FuncName, TArg1, Arg1Name ) \
TRet FuncName( TArg1 Arg1Name ) { \
LOCK; \
TRet ret = FuncName##_Locked( Arg1Name ); \
UNLOCK; \
return ret \
} \
TRet FuncName##_Locked(TArg1 Arg1Name )
#define LOCKED_FUNCTION_ARG2(TRet FuncName, TArg1, Arg1Name, TArg2, Arg2Name) \
//...etc
but you will need 1 macro for every count of arguments (and the function should have a return type).
example usage:
LOCKED_FUNCTION_ARG1(int, f1, int, myintarg)
{
//unchanged code here
}
Define the wrapper like this:
class SemaphoreWrapper
{
private:
semaphore &sem;
public
SemaphoreWrapper(semaphore &s)
{
sem = s;
sem.lock();
}
~SemaphoreWrapper()
{
sem.unlock();
}
}
Then simply create an instance of SemaphoreWrapper within each function:
void func1()
{
SemaphoreWrapper(global_semaphore);
...
}
The constructor and destructor of SemaphoreWrapper will take care of the lock/unlock functionality.
Don't.
You cannot go from single-threading to multi-threading by spewing a few locks here and there and hope for the best.
Are you sure that there are no two functions that share a global variables ? C-functions are notorious for using statically allocated buffers.
Mutexes are not normally reentrant. Therefore if you decorate
f1
andf2
and one calls the other, you'll deadlock. You can, of course, use more expensive reentrant mutexes.
Multithreading is hard, at the best of times. It usually requires to understand and adjust the flow of execution.
I have a hard time imagining that throwing a few locks around would work for the best.
And that is obviously discounting the fact that if the functions were not crafted with MT in mind, they might well be slower (with all those mutex operations) and you won't, therefore, reap much benefit.
If you really need your semaphore, have the client lock it. He should know when to lock and when not to.
You could write wrapper functions, e.g.:
int f1_locked(int x) {
lock(..);
int r=f1(x);
unlock(..);
return r;
}
With the preprocessor you may save some work.
EDIT
As someone stated, it would be better to move the implementations into library-internal functions, and present the wrappers to library users, e.g.:
// lib exports the wrapper:
int f1(int x) {
lock(..);
int r=f1_unlocked(x);
unlock(..);
return r;
}
// for library internal use only:
int f1_unlocked(int x) {
...
}
This has the additional advantage, that calls from and to lib internal functions do not need superfluous locking (which may be possible or not, this depends..), e.g.:
void f2_unlocked() {
...
f1_unlocked();
...
}
void f2() {
lock();
f2_unlocked();
unlock();
}
Decorators are strictly a language feature that provides syntactics sugar for the underlying semantics. The underlying semantics you can get in C++: just wrap the function appropriately; the syntactic sugar you can’t get.
A better alternative would be to create an appropriate code design that supports your use-case, e.g. by ineritance and the decorator pattern. This doesn’t necessarily imply class inheritance – the pattern can also be realized using class templates.
As for your specific use case, better alternatives have already been posted.
It seems to me that what you want to do is Aspect Oriented Programming (AOP). There are few AOP frameworks in C and C++, but from what I saw a few months ago I think the AspectC++ project provides a nice implementation of the AOP concepts. I have not tested it in production code though.
精彩评论