
Once upon a time, I mentioned a utility to create C functions from a function object. And recently someone asked for that code (sorry I don't who you are). So I cleaned it up so it can be used outside of my development tree. You can get the code in the vault <http://tinyurl.com/2l29km> as the c_function.hpp file. Enjoy. And comments welcome. -- -- Grafik - Don't Assume Anything -- Redshift Software, Inc. - http://redshift-software.com -- rrivera/acm.org - grafik/redshift-software.com -- 102708583/icq - grafikrobot/aim - grafikrobot/yahoo

AMDG Rene Rivera <grafikrobot <at> gmail.com> writes:
Once upon a time, I mentioned a utility to create C functions from a function object. And recently someone asked for that code (sorry I don't who you are). So I cleaned it up so it can be used outside of my development tree.
You can get the code in the vault <http://tinyurl.com/2l29km> as the c_function.hpp file.
Enjoy. And comments welcome.
It's not thread safe and the only way I can see to make it so is to have a fixed set of functions and allocate/deallocate In Christ, Steven Watanabe

Steven Watanabe wrote:
It's not thread safe and the only way I can see to make it so is to have a fixed set of functions and allocate/deallocate
Why would it need to be thread safe? Usually one puts thread safety above this kind of utility layer. -- -- Grafik - Don't Assume Anything -- Redshift Software, Inc. - http://redshift-software.com -- rrivera/acm.org - grafik/redshift-software.com -- 102708583/icq - grafikrobot/aim - grafikrobot/yahoo

-----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Rene Rivera
Steven Watanabe wrote:
It's not thread safe and the only way I can see to make it so is to have a fixed set of functions and allocate/deallocate
Why would it need to be thread safe? Usually one puts thread safety above this kind of utility layer.
Well if you need to implement a C-callback that doesn't have a void * data argument.

Sohail Somani wrote:
Steven Watanabe wrote:
It's not thread safe and the only way I can see to make it so is to have a fixed set of functions and allocate/deallocate Why would it need to be thread safe? Usually one puts thread safety above this kind of utility layer.
Well if you need to implement a C-callback that doesn't have a void * data argument.
You put the thread safety in what the c-function calls. Now if the question really is that the setting of the function object instance isn't thread safe... You would make the code that calls make_c_function thread safe, by using locks for example. Note, calling the c-function, and hence the function object, is thread safe AFAIK. -- -- Grafik - Don't Assume Anything -- Redshift Software, Inc. - http://redshift-software.com -- rrivera/acm.org - grafik/redshift-software.com -- 102708583/icq - grafikrobot/aim - grafikrobot/yahoo

AMDG Rene Rivera <grafikrobot <at> gmail.com> writes:
Sohail Somani wrote:
Steven Watanabe wrote:
It's not thread safe and the only way I can see to make it so is to have a fixed set of functions and allocate/deallocate Why would it need to be thread safe? Usually one puts thread safety above this kind of utility layer.
Well if you need to implement a C-callback that doesn't have a void * data argument.
You put the thread safety in what the c-function calls. Now if the question really is that the setting of the function object instance isn't thread safe... You would make the code that calls make_c_function thread safe, by using locks for example. Note, calling the c-function, and hence the function object, is thread safe AFAIK.
That isn't what I meant. If void(*f)(); { scoped_lock lock(m); f = make_c_function(...); } use f were sufficient I wouldn't have a problem. Unfortunatly, it is necessary to retain the lock until you are done with f. In Christ, Steven Watanabe

Steven Watanabe wrote:
That isn't what I meant. If
void(*f)(); { scoped_lock lock(m); f = make_c_function(...); } use f
were sufficient I wouldn't have a problem.
Unfortunatly, it is necessary to retain the lock until you are done with f.
Define "done with f". -- -- Grafik - Don't Assume Anything -- Redshift Software, Inc. - http://redshift-software.com -- rrivera/acm.org - grafik/redshift-software.com -- 102708583/icq - grafikrobot/aim - grafikrobot/yahoo

AMDG Rene Rivera <grafikrobot <at> gmail.com> writes:
Steven Watanabe wrote:
That isn't what I meant. If
void(*f)(); { scoped_lock lock(m); f = make_c_function(...); } use f
were sufficient I wouldn't have a problem.
Unfortunatly, it is necessary to retain the lock until you are done with f.
Define "done with f".
Reach a point where you will not call it again. (I'm refering the the value returned by make_c_function not the variable f) In Christ, Steven Watanabe

Steven Watanabe wrote:
Rene Rivera <grafikrobot <at> gmail.com> writes:
Steven Watanabe wrote:
Unfortunatly, it is necessary to retain the lock until you are done with f. Define "done with f".
Reach a point where you will not call it again. (I'm refering the the value returned by make_c_function not the variable f)
How do you determine that you won't call it again? What do you want to happen at that time? What do you want to happen after that time? Sorry if I keep asking, but it's hard to answer questions with essentially no information as to what you want to do. -- -- Grafik - Don't Assume Anything -- Redshift Software, Inc. - http://redshift-software.com -- rrivera/acm.org - grafik/redshift-software.com -- 102708583/icq - grafikrobot/aim - grafikrobot/yahoo

AMDG Rene Rivera <grafikrobot <at> gmail.com> writes:
Steven Watanabe wrote:
Rene Rivera <grafikrobot <at> gmail.com> writes:
Steven Watanabe wrote:
Unfortunatly, it is necessary to retain the lock until you are done with f. Define "done with f".
Reach a point where you will not call it again. (I'm refering the the value returned by make_c_function not the variable f)
How do you determine that you won't call it again? What do you want to happen at that time? What do you want to happen after that time? Sorry if I keep asking, but it's hard to answer questions with essentially no information as to what you want to do.
struct tag {}; void f(int (&array)[100][3], unsigned key_index) { assert(key_index < 3); std::qsort(array,100,sizeof(int[3]), make_c_function<tag,void(const void*,const void*)>( ll::static_cast_<const int*>(_1)[key_index] < ll::static_cast_<const int*>(_1)[key_index])); } In Christ, Steven Watanabe

Steven Watanabe wrote:
struct tag {};
void f(int (&array)[100][3], unsigned key_index) { assert(key_index < 3); std::qsort(array,100,sizeof(int[3]), make_c_function<tag,void(const void*,const void*)>( ll::static_cast_<const int*>(_1)[key_index] < ll::static_cast_<const int*>(_1)[key_index])); }
struct tag { boost::mutex lock; }; tag tag_; void f(int (&array)[100][3], unsigned key_index) { assert(key_index < 3); boost::mutex::scoped_lock l(tag_.lock); std::qsort(array,100,sizeof(int[3]), make_c_function<tag,void(const void*,const void*)>( ll::static_cast_<const int*>(_1)[key_index] < ll::static_cast_<const int*>(_1)[key_index])); } Not that the use case makes any sense to me :-( So I'm still guessing as to what you want to achieve with thread safety here. PS... This thread reminds me of the ##c++ irc conversations. -- -- Grafik - Don't Assume Anything -- Redshift Software, Inc. - http://redshift-software.com -- rrivera/acm.org - grafik/redshift-software.com -- 102708583/icq - grafikrobot/aim - grafikrobot/yahoo

Rene Rivera wrote:
Not that the use case makes any sense to me :-( So I'm still guessing as to what you want to achieve with thread safety here.
The idea is that the function f - if we use an ordinary C callback instead of make_c_function - is thread safe when called at the same time from multiple threads to sort different arrays. make_c_function breaks this thread safety because of its assignments to the underlying boost::function. I think.

Peter Dimov wrote:
Rene Rivera wrote:
Not that the use case makes any sense to me :-( So I'm still guessing as to what you want to achieve with thread safety here.
The idea is that the function f - if we use an ordinary C callback instead of make_c_function - is thread safe when called at the same time from multiple threads to sort different arrays. make_c_function breaks this thread safety because of its assignments to the underlying boost::function.
I think.
I don't buy that one. At call time the thread safety of a plain C function is the same as C function returned from make_c_function, in a transitive sense. If there's any thread unsafeness it's in the context used for the bind, if there is one. That context has the same thread safety as any other context one would need to make use of a C function to execute the same C++ code as the bind would call, or any other piece of C or C++ code. The one caveat would be if boost::function is non-const in behavior for every call. -- -- Grafik - Don't Assume Anything -- Redshift Software, Inc. - http://redshift-software.com -- rrivera/acm.org - grafik/redshift-software.com -- 102708583/icq - grafikrobot/aim - grafikrobot/yahoo

So if I understand this correctly, you basically have something similar to this (but using template magic): static boost::function<void ()> b_void_func; static boost::function<void (int)> b_void_func_int; static boost::function<void (int,int)> b_void_func_int_int; // etc for different return types and param types, // magically generated as needed by the compiler and you also have (and also magically generated): static void c_func() { return b_func(); } static void c_func_int(int i) { return b_func_int(i); } // etc for all necessary combinations (And you also have statics for each tag: static boost::function<void()> b_func_magictag; but we can leave that aside for now) The important part is that each function pointer is *static*. So make_c_function() in effect 'finds' the right c_func and returns it: SomeFunctionType make_c_function(boost::function f) { // using template magic set the correct ***static*** boost function: // let's pretent it is b_func b_func = f; // and magically return the right c_function, let's say c_func: return c_func; } now imagine 2 threads // thread 1 cf1 = make_c_func(mybind1) cf1(); // call cf1 - it calls mybind1 // thread 2 cf2 = make_c_function(mybind2) cf2(); // calls cf2 - ie mybind2, right??? but if the threads happen to be running at the same time: // thread1 gets a timeslice: cf1 = make_c_function(mybind1); // internally sets *global* b_func = mybind1 // say thread2 gets a timeslice now: cf2 = make_c_function(mybind2); //sets *global* b_func = mybind2 cf1(); // calls c_func, which cals mybind1? NO it calls mybind2. That's the threadsafety problem. So, it's OK for some uses, and maybe using tags you could keep track of your uses better (ie this thread always uses this tag, etc), but if you are dynamically creating threads (ie 1 per server request, etc), then this can't easily be used. Tony

Gottlob Frege wrote: [snip]
That's the threadsafety problem.
I'm starting to get the impression that this is all a problem with the term "make" in make_c_function. People are reading more into that term than I do. The utility doesn't create anything new, in the allocation sense, it gives you a tagged function. It happens to provide a convenient interface for associating it a singleton function object. But at least I understand what Steven's point/use is now :-)
So, it's OK for some uses, and maybe using tags you could keep track of your uses better (ie this thread always uses this tag, etc),
Yes. And unless you do some serious code generation magic this the only possible use you can achieve with the static binding model of C/C++. (in the symbol binding sense)
but if you are dynamically creating threads (ie 1 per server request, etc), then this can't easily be used.
Correct. And I never designed it for that. My use case is for creating one time bindings for plugins, i.e. function tables that map to C++ singletons. And yes, I wish it where also possible to just make free floating C functions. But that's not easily possible, since you can't make existing C function pointers bigger at will. -- -- Grafik - Don't Assume Anything -- Redshift Software, Inc. - http://redshift-software.com -- rrivera/acm.org - grafik/redshift-software.com -- 102708583/icq - grafikrobot/aim - grafikrobot/yahoo

On 4/21/07, Rene Rivera <grafikrobot@gmail.com> wrote:
Gottlob Frege wrote: [snip]
[snip]
And yes, I wish it where also possible to just make free floating C functions. But that's not easily possible, since you can't make existing C function pointers bigger at will.
But you can use tss and have no problems of that kind. But you will surely have other problems, you now can't pass the function to another thread, which is quite hard to guarantee. But I believe you can guarantee thread-safety between codes of different translation units. Inside the same translation unit, any "creation" of a new c-adapter would replace the old one, but two translation units wouldn't mess with the other. Which may be enough. Sorry if the library already does this, I didnt look into the implementation.
-- -- Grafik - Don't Assume Anything -- Redshift Software, Inc. - http://redshift-software.com -- rrivera/acm.org - grafik/redshift-software.com -- 102708583/icq - grafikrobot/aim - grafikrobot/yahoo
Best regards, -- Felipe Magno de Almeida

On 4/20/07, Rene Rivera <grafikrobot@gmail.com> wrote:
And yes, I wish it where also possible to just make free floating C functions. But that's not easily possible, since you can't make existing C function pointers bigger at will.
It's not that hard, but it tends to be very platform dependent. And now with all the security features in the OSes lately, it is hard to convince the OS to call your function that you have created 'by hand' on the heap (ie not in an executable memory block). Tony

AMDG Rene Rivera <grafikrobot <at> gmail.com> writes:
struct tag { boost::mutex lock; };
tag tag_;
void f(int (&array)[100][3], unsigned key_index) { assert(key_index < 3); boost::mutex::scoped_lock l(tag_.lock); std::qsort(array,100,sizeof(int[3]), make_c_function<tag,void(const void*,const void*)>( ll::static_cast_≤const int*>(_1)[key_index] < ll::static_cast_≤const int*>(_1)[key_index])); }
Not that the use case makes any sense to me So I'm still guessing as to what you want to achieve with thread safety here.
That's basically what I said needs to be done: retain the lock until you are done with the function. The critical section is now way bigger than I would like. The only way I know to avoid this is to create a fixed pool of functions and have an additional routine that releases a function pointer so that it can be safely reused. struct tag {}; void f(int (&array)[100][3], unsigned key_index) { assert(key_index < 3); void (*f)(const void*,const void*) = make_c_function<tag,void(const void*,const void*)>( ll::static_cast_<const int*>(_1)[key_index] < ll::static_cast_<const int*>(_1)[key_index]); std::qsort(array,100,sizeof(int[3]),f); release_c_function<tag>(f); } If I ever use make_c_function it would probably be in some use like this (to circumvent the fact that a function pointer cannot have data associated) rather than for callacks that are created once and never changed (such as for ios_base::register_callback) In Christ, Steven Watanabe
participants (6)
-
Felipe Magno de Almeida
-
Gottlob Frege
-
Peter Dimov
-
Rene Rivera
-
Sohail Somani
-
Steven Watanabe