[function] "paranoia" vs efficiency

As far as I can see boost::function<>::operator() performs: if (this->empty()) boost::throw_exception(bad_function_call()); before invoking the stored function (object)... My question is why or why only this "checked"/"safe" approach/method of invocation exists? Because this: - causes (extra) code branching - causes the compiler to insert exception throwing and handling code in surrounding/calling code (which need not otherwise be if the function stored in the boost function instance is itself nothrow) - inserts a call to the bad_function_call constructor - disables the compiler from inlining the function (MSVC atleast) - disables NVRO (MSVC atleast, http://msdn.microsoft.com/en-us/library/ms364057(VS.80).aspx#nrvo_cpp05_topi... ) while at the same time I would most often consider calling a "null-function-pointer" a programmer error (that would therefor need to be "handled" with an assert not an exception), not a runtime 'unpredictable' exception... ...IMO the default should be not to include this check (i.e. use an assert) and provide a safeInvoke() type of method for those users and cases that actually require/want this type of check/behaviour... ...or atleast "it would be welcomed" if a non-checking unsafeInvoke() type of method would be provided for those that are 'sure' that the boost::function<> object is valid (for example in the line before i've just performed the same check to decide whether to call the function object or not)... -- "That men do not learn very much from the lessons of history is the most important of all the lessons of history." Aldous Huxley

Domagoj Saric wrote:
As far as I can see boost::function<>::operator() performs: if (this->empty()) boost::throw_exception(bad_function_call()); before invoking the stored function (object)... My question is why or why only this "checked"/"safe" approach/method of invocation exists?
BOOST_ASSERT(!empty()) would be better in my opinion indeed, but it's hard to make that change now for compatibility reasons.

"Mathias Gaunard" <mathias.gaunard@ens-lyon.org> wrote in message news:ha52jl$4oc$1@ger.gmane.org...
Domagoj Saric wrote:
As far as I can see boost::function<>::operator() performs: if (this->empty()) boost::throw_exception(bad_function_call()); before invoking the stored function (object)... My question is why or why only this "checked"/"safe" approach/method of invocation exists?
BOOST_ASSERT(!empty()) would be better in my opinion indeed, but it's hard to make that change now for compatibility reasons.
considering the improvement is more than cosmetic i'm (always) for breaking changes (but then again i do not, yet, 'live' in the 'conservative corporate universe' where you bow to BackwardCompatibility ;)... but, come to think of it, there is an (almost) win win solution... what if the check-and-throw is not performed with an explicit if-and-throw but rather a default constructed or emptied boost::function<> object does not actually become 'empty' (in the sense that it holds a null pointer/object) but rather it holds a default/static/shared function pointer/object that does the actual throw... ...this is an 'almost' win win solution because you now get a thin/inlined away function<>::operator() and the same 'safe' behaviour but are still left with exceptions (the compiler still must 'regard' operator() as a possibly throwing function)... if we go further we could add one more useful behaviour without breaking changes...the default behaviour could be parameterized (with the default being the exception throw for backward compatibility) to be: 1. throw 2. assert in debug - access violate on a null pointer in release 3. nop the third option would eliminate the tipical: { if ( myFunction ) myFunction(); } code (it would, in fact/actually eliminate the branching from the generate code itself, because it would be implemented by calling a function through a pointer, that you would do anyway, that does nothing, simply returns)... now, if we make the parameter for the OnEmpty behaviour a template parameter we could have a truly win-win solution (if ofcourse, one could implicitly add the __declspec( nothrow ) or equivalent function declaration 'decorator' to the boost::function<>::operator() based on a template parameter and/or actual used function object type)... seeing this article http://www.codeproject.com/KB/cpp/fastdelegate2.aspx makes me realize (or just think?) that the same approach can be used to create multicast functions/delegates (didn't yet have time to check how was it implemented in that article/if there is a better solution) without 'breaking changes', or creating a new boost::multicast_function<> class or adding any runtime overhead to boost::function<> instances that would not use multicasting capabilities (multicasting would/could be implemented by a special internal function object that calls the many stored functions in a list, or thru a function chain of some sort...) -- "That men do not learn very much from the lessons of history is the most important of all the lessons of history." Aldous Huxley

On Fri, Oct 2, 2009 at 10:40 AM, Domagoj Saric <dsaritz@gmail.com> wrote:
"Mathias Gaunard" <mathias.gaunard@ens-lyon.org> wrote in message news:ha52jl$4oc$1@ger.gmane.org...
Domagoj Saric wrote:
As far as I can see boost::function<>::operator() performs: if (this->empty()) boost::throw_exception(bad_function_call()); before invoking the stored function (object)... My question is why or why only this "checked"/"safe" approach/method of invocation exists?
BOOST_ASSERT(!empty()) would be better in my opinion indeed, but it's hard to make that change now for compatibility reasons.
considering the improvement is more than cosmetic i'm (always) for breaking changes (but then again i do not, yet, 'live' in the 'conservative corporate universe' where you bow to BackwardCompatibility ;)...
but, come to think of it, there is an (almost) win win solution... what if the check-and-throw is not performed with an explicit if-and-throw but rather a default constructed or emptied boost::function<> object does not actually become 'empty' (in the sense that it holds a null pointer/object) but rather it holds a default/static/shared function pointer/object that does the actual throw... ...this is an 'almost' win win solution because you now get a thin/inlined away function<>::operator() and the same 'safe' behaviour but are still left with exceptions (the compiler still must 'regard' operator() as a possibly throwing function)... if we go further we could add one more useful behaviour without breaking changes...the default behaviour could be parameterized (with the default being the exception throw for backward compatibility) to be: 1. throw 2. assert in debug - access violate on a null pointer in release 3. nop
the third option would eliminate the tipical: { if ( myFunction ) myFunction(); } code (it would, in fact/actually eliminate the branching from the generate code itself, because it would be implemented by calling a function through a pointer, that you would do anyway, that does nothing, simply returns)...
now, if we make the parameter for the OnEmpty behaviour a template parameter we could have a truly win-win solution (if ofcourse, one could implicitly add the __declspec( nothrow ) or equivalent function declaration 'decorator' to the boost::function<>::operator() based on a template parameter and/or actual used function object type)...
seeing this article http://www.codeproject.com/KB/cpp/fastdelegate2.aspx makes me realize (or just think?) that the same approach can be used to create multicast functions/delegates (didn't yet have time to check how was it implemented in that article/if there is a better solution) without 'breaking changes', or creating a new boost::multicast_function<> class or adding any runtime overhead to boost::function<> instances that would not use multicasting capabilities (multicasting would/could be implemented by a special internal function object that calls the many stored functions in a list, or thru a function chain of some sort...)
That is something I have been curious about since I first looked at the Boost.Function source. I was curious why it did not use any real policies like most other things in boost, for example one policy could enforce it to only use function pointers (no functors) for situations where you know it would only be pointers so you could get a speed advantage, or ones for other purposes (such as the multi-cast example above, that sounds fascinating, I would use it). And I *love* the idea about using a nop static func that throws, or perhaps asserts, all depending on policy settings for example. :)

... ... ok ... the fourth attempt in two days ... now i'll try without an attachment (rar and tgz did not pass) and put the source in the boostpro vault ( home / function objects / new_function_implementation_proposal.tgz ) ... ... "OvermindDL1" <overminddl1@gmail.com> wrote in message news:3f49a9f40910021215g981da6cxd93c43ac52fee5cc@mail.gmail.com...
That is something I have been curious about since I first looked at the Boost.Function source. I was curious why it did not use any real policies like most other things in boost
it seems to me rather that there is no "clear cut" policy (pardon the pun :) about that (the policy approach) in boost...for example the shared_ptr documentation states that shared_ptr uses a more joe sixpack approach with a "forced" dynamic deleter on purpose, to achieve a simpler interface and a more 'stable' abi (the point seems moot imho, the same could be achieved with default template parameters while retaining compile-time configurability)...
for example one policy could enforce it to only use function pointers (no functors) for situations where you know it would only be pointers so you could get a speed advantage
well if you are going to use only (fixed signature) function pointers there's no need for boost::function in the first place (or so it seems to me)...
And I *love* the idea about using a nop static func that throws, or perhaps asserts, all depending on policy settings for example. :)
well...here it is..."and more" ;-) the boost::function documentation states that virtual functions are not used because they cause code bloat but comparing the actual binaries and code generated i found boost::function to actually be worse in that respect than the std::tr1::function implementation provided with msvc++ 9.0 SP1 (that does use virtual functions)... ...then, looking at the code in more detail i found a number of culprits/things that could be improved so i rewrote most of it and here is my proposal for a new boost::function implementation... { before continuing just a few terminology definitions (just for this post for easier reading): looking at the boost/tr1::function<> design/idea we can identify two 'domains', the "front side" or the interface side or the "fixed type" or ABI side...the interface that the user sees and the code that exists and executes _before_ the call through a function pointer in the vtable (e.g. the code in operator() before the invoke call is made, or code in operator= before calls to the manager are made ...); ...and the "back side" or that which stands behind the 'function pointer brick wall' (the actual invoker and the actual manager), which changes with each assignement, which, at the point of change or creating has all the type information it wants... } the change list is as follows: - there was a lot of functionality in the function_template.hpp (in the main boost::function<> template class) that was not related/specific to the particular boost::function<> instantiation and could be extracted into the non-template function_base base class (and was causing actual bloat as the compiler/linker did not/could not merge it)...moved as much of such code as possible into the base header/class - along with that 'binary code duplication' caused by template code bloat there was also a certain amount of source code duplication (like same descisions being made both at the "front side" and the "back side", or type information managing implemented both in the 'shared' manager and again in each type specific manager...) - there is now only a single invoker template (actually two, the void returning version of the first one) - there is now only one vtable type/struct (the one in the base header) - the invoker pointer was moved into the base vtable and was placed at the beginning of the vtable so that the vtable pointer would point to it directly (to avoid the pointer offset calculation mentioned in the "[optional] layout" thread) - the invoker pointer/signature was changed to be a member function of the function_buffer so that the thiscall calling convention would be used. according to this information http://en.wikipedia.org/wiki/X86_calling_conventions#thiscall this makes no difference for gcc but does for MSVC (and x86) because now the pointer to the function_buffer is passed in the ECX register making the stack look exactly (or more closely, depending on the target) as it needs to be to invoke the target in the invoker function providing for a more direct call of the target (with no or less stack adjustment/additional pushing/poping) - the manager design was changed to allow easier splitting and extracting of functionality (and in the end for both smaller and faster code)...previous manager design used a combination of one function pointer and 'message'/'op-code' passing and switching...this, among other things, caused separate manager code to be generated for every different type of object assigned to the boost::function<> object because the same function was 'managing' both the (obiously different) type information and the move/clone/destroy functionality (which is actually shared for a vast majority of assigned types of objects)...this was changed so that the op-codes were replaced with an enlarged vtable with separate pointers/functions for move, clone, destroy and type information functionality...type information functionality was also changed so that the "back side" only creates an object containing all the relevant information and passes it to the "front side" that can then use how and _if_ it needs it (this way the type comparison code for the target() member function is only generated if it is actually used) ...cv-qualifier information for objects stored as references is also no longer saved in two run-time bools but is encoded in the type of the "back side" (so function_buffer::type_t is now typed_functor, and no longer needs to reside in the function_buffer and function_buffer::obj_ref_t is gone all together)... - added more managers (total of 5, previously there were 3) that work with as little type information as possible (the purpose and implementation should be obvious from the source and acompanying comments) - the vtable static initialization was moved outside the vtable_for_functor() function to ensure that static initialization was actually always used (with increased numer of function pointers/size of the vtable msvc would start using lazy initialization...like in a 'normal' "meyers singleton") - the "has_trivial_copy_and_destroy" optimization and the encoding of that information into the vtable pointer was removed because in practice it was actually a pessimization (atleast/especially with the new design)...if it somehow turns out usefull at some later time it can now be more easily implemented (e.g. with a larger vtable, the trivial functionality could simply use null pointers...)... - assignment functionality was moved from the (no longer present) boost::function<> instantiation-specific vtable struct (BOOST_FUNCTION_VTABLE) into the appropriate functor manager classes in the base header - assignment functionality was also heavily optimized (although there is still room for improvement)...until now the default 'safe' approach of <copy-constructing a temporary and calling swap> was always used (which resulted in hundreds if not a few thousands of asm instructions getting executed, and was part of the template so it caused bloat)...now this is done only for function objects that have nontrivial copy-constructors and/or do not fit in the function_buffer (and is not part of the template)...for the often case of simple binds and function pointers assignment (and construction) now results in a call to the nothrow, and most often trivial, destroy function and a couple of movs... - empty-invocation handling was changed to no longer place an "if (empty) throw" section in the operator() (of the template class, thus pretty much adding to bloat) but to use (assign to itself an) "EmptyHandler" function object when in the empty state (that then gets invoked and does 'its thing')...(this also removed the need for the msvc 6.0 workaround for the in/out of class definition of the operator() function - as it is now only a thin wrapper it can, actually should, be safely inlined)... - the three main changes above (managment, assignment and invocation) pretty much solved the issues of the, what i call, 'bad code bloat'...i.e. the code that is redundant but does actually intertwine with usefull code and thus gets executed, pollutes the cache etc...but there is also a different kind of bloat that is actually probably better called 'data bloat' which only increases the binary size (maybe perhaps in this case also slows down dynamic_cast performance) that is in this case caused by rtti records...boost/tr1::function<> causes rtti records to be created even for non polymorphic objects because of the forced use of typeid...this can be 'very' significant for templated function objects (that have very long type names)...because of this i added BOOST_NO_TYPEID support that disables the type information functionality...in a real life example where i have only two boost::function<> instantiations/signatures to which i assign mpl generated functors...switching from the default/current boost::funtction<> implementation to this new one using BOOST_NO_TYPEID and the nothrow policy (described later) a ~300kb binary shrunk by ~30kb (~10%)... ...this (no) type information should preferably be made a matter of policy so that it can be selectively, not globally, disabled...it could be usefull for libraries like boost::thread or boost::signals that use boost::function and do not need type information...then they could "hold on to more type information for longer" to possibly create more efficient boost::function targets without fear of code-bloat... - explicit exception handling was replaced with guard objects - const's were added, mutable's removed and pointers converted to references where ever possible - the generic has_empty_target() function was fixed for MSVC to always use the ( void const * ) overload because MSVC does not inline vararg functions so it was always generating calls to a function that only returns false (so it was unable to remove code that does not execute for the false case)...(perhaps this - an additional template parameter was added to the main boost::function<> template class called PolicyList (a list to cut down on the number of parameters) that is expected to be a boost::mpl::map-like container of policies. currently two policies exist: - how to handle invocation of empty function objects (three default handlers are provided: throw_on_empty, assert_on_empty and nop_on_empty) - whether the function<> instantiation should 'report'/mark its operator() as nothrow and accept only nothrow targets (for supported compilers) - for the nothrow functionality to be any usefull the compiler needs to provide a way to mark a function as nothrow but without it being a pessimization as is the case with current exception specifications (e.g. MSVC __declspec( nothrow )) - for the nothrow functionality to be safe (not allow assignment of function objects that can throw to a nothrow marked boost::function<>) we need a way do detect nothrow functions...i could come up with two ways: - the 'exception specification shadow type system' ( http://www.gotw.ca/publications/mill22.htm ) could provide a compile-time method that would work for functions/function pointers...i tried it with comeau online and it seemed to work ok (it would properly report as a compile time error an attempt to assign a function with a mismatched exception specification) but with msvc it, ofcourse, did not...i pretty much 'broke my teeth' trying to make it work...only to discover that msvc, on the one hand incorrectly allows a throw(...) marked function to be assigned to a throw() marked pointer but on the other hand allows a typedef with an exception specification and "mangles the living hell" out of the type of any function that has an exception specification (even its builtin __declspec( nothrow ))... ...it actually changes the type of the function (you can see it in compiler error messages, for example, like "...with T = void (* __cdecl) ( int ) throw() ...") but it does it in some weird and inconsistent way: for example you can use this type difference to detect/differentiate between throw() marked and unmarked functions in a template function that accepts a pointer to a function _but_ only on first invocation of that call...in other words if that function has a compile-time conditional statement that branches depending if the passed function pointer is marked as throw() or not only a _single_ implementation of that function will be generated depending on with what function pointer it was called for the first time...in other words if we have void f(); void ff() throw(); template <class F> bool g( F const & f ) { if ( <f has throw() decoration> ) return true; else return false; } g() will always return true if we first call it with g(&ff) (no matter with what we call it with later) and will always return false if we first call it with g(&f)...?!? ...even worse than that is that the type of ff() in the above case is so 'mangled' that it is no longer 'recognized' by even the tr1 type traits distributed with the compiler (is_pointer, is_function, has_trivial_copy&co. all return false)...so i had to add a workaround is_msvc_exception_specified_function_pointer<> 'quasi-trait' to recognize such objects as functions (otherwise they would be treated as completely generic objects)...perhaps this or some smarter workaround should be added to boost::type_traits also... ...perhaps someone with gcc experience could try and make a proper implementation for a conforming compiler although i don't know if it's worth it, considering it would only work with plain function pointers and a conforming compiler would also probably have a conforming implementation of exception specifications which would render this a pessimization in most cases... - the other way or method relies on no "language dark corners" but on the ability of the compiler optimizer to detect nothrow functions and remove try-catch blocks around them and then the linker's ability to merge identical but differently named functions...this method is always safe in that it will never report that a function object is nothrow if in fact it is not (unless of course the compiler and/or the linker are so terribly broken but then almost anything produced by such a compiler/linker would be broken)...it is also tested to work as expected with msvc 9.0 sp1...but it has the drawbacks that it is "link-time knowledge" so it works at runtime and thus slightly complicates the assignement and construction code with the runtime is-throwable check and adds a certain amount of the "less bad code bloat" (three small functions are generated for every type of assigned function object, two of which are never executed, only their addresses are compared, and the third is a 5 instruction static bool initializer executed only once at startup) ...in any case, if a boost::function<> is instantiated with the nothrow policy it will use the above tests to accept (on construction and assignement) only nothrow targets...if it cannot be deduced that a target is nothrow, the EmptyHandler will be assigned >and then executed< (just my current idea)... - fixed the BOOST_NO_STD_TYPEINFO for the defined( BOOST_MSVC ) && !_HAS_EXCEPTIONS case (seems that MSVC (9.0 SP1) standard headers do not import type_info into the std namespace when STL exceptions are disabled)...this should probably be moved into the BOOST_NO_STD_TYPEINFO definition site/header - allocator support is currently disabled/commented out...i can add it/ properly implement it if the changes so far get accepted (this should probably be merged into one code base, the default being the use of the std::alocator...)... ...that would be about it for the most important changes/proposals in boost::function<> itself... a few more notes: - the default interface and behaviour remain the same (current code would work the same without modification with these changes, "or so it seems to me") - the empty handler objects are required to satisfy the is_stateless<> condition - all function objects used with boost::function<> are expected to have a nothrow destructor - the swap and non-trivial assignement function do not offer the strong exception safety guarantee in the special case when one or both of the objects at stake is or has (if it is a boost::function<> object "holding" some function object) a function object that fits in the function_buffer but does not have a nothrow copy constructor and therefore does not have a no-fail move operation...in this case it can happen that the final move operation (in the swap procedure) from the tmp object to *this can fail and then the attempt to restore (move) the original *this from a backup location can also fail leaving us with no valid object in *this...in this situation the *this function object is cleared (the EmptyHandler is assigned)...as far as i could tell the original boost::function<> implementation had the same behaviour... - a few more ideas of what can be made a matter of policy: size and alignement of the function buffer (perhaps some library uses only complex targets so it would benefit if the function_buffer optimization would be eliminated all together...on the other some other library would benefit if the buffer would be better aligned or enlarged by "just one int" and then no memory allocation would take place for that library ...etc...) - there is the ever present problem of what to return from empty handlers for non-void and non-default constructible return types... ... as far as performance tests are concerned: - i modified the test code presented here http://marc.info/?l=boost&m=118835709647084&w=2 to work with boost::signals2 with the dummy mutex/no threading support and a simple recompilation with the new boost::function<> code yielded a ~10% boost, changing the default policy to be nothrow the boost doubled to ~20%...i suspect this to be because of the temporary shared_ptr() that is constructed prior to the call (and is imho probably unnecessary anyway in the single threaded case)...in the generic 'throwable' case the compiler had to insert EH code that would call the shared_ptr<> destructor in case of an exception... - apart from the EH issue mentioned above a performance boost can come from: - the simpler invoker "back side"/setup code for compilers that benefit from the thiscall change (which becomes irrelevant for function object targets that get inlined into the invoker anyway) - the simpler invoker "front side"/operator() which is mostly benefitial for situations like the one described above where you make a single call to a boost::function<> in a non-inlined enclosing function...in situations where a boost::function<> is called in a tight loop the difference becomes much less noticable, like 1-2% (atleast with msvc 9.0 that is able to move most of the previous operator() complexities out of the main loop path) - i suspect that copy/assignement would be faster in orders of magnitude for most real world cases but didn't have time to test it... - space improvements/issues are probably best examined/appreciated with a profiler capabale of static analysis (to compare the amount of template code generated) there is also one more important source of performance, as is often the case, the use of static/compile-time information..with function pointer/bind expression targets... as the boost::function<> documentation mentiones using/assigning function pointers adds another indirect call...but we are usually assigning pointers to exact/specific/known at the assign site functions...if we could somehow assign them using a template parameter that one extra indirect call would be gone (additionally the compiler could perform the nothrow analysis on the compile-time know function target, which it cannot on a plain pointer)...the problem is we need the type in advance to have a non-type template parameter...so we have to resort to tedious things like bf.assign( &f ).static_assign<&f>() (don't know if constexpr-essions would help here)... ...but with boost::function<> we already know the type for the trivial case of assignment of a global function with an exact signature, and can also trivialy extend it for the case of member function that matches the exact signature... ...that's why i added two template overloads for the assign member function so the following can compile and work properly: class A { void mem_func( int ); } void global_func( int ); void main() { boost::function<void (int)> bf; bf.assign<&global_func>(); bf( 1 ); A a; bf.assign<A, &A::mem_func>( a ); bf( 2 ); } using 'static assignment' the difference between old and new boost::function<> can be even up to two calls less...for example (depending on compiler switches and day of the month) the call stack in the first case (that would then ofcourse use the dynamic bf.assign( &global_func )) above could look like this: { global_func() bf::invoker() bf::operator() main() } for the previous/current official implementation and like this: { global_func() main() } with the new implementation... motivated by the above i also added a proposal/extension for boost::ref() (here called boost::sref() with a helper macro BOOST_SREF) to offer a generic way for creating static references as well as a 'hack modification' (inclusion into a test namespace with different configuration macors) of the mem_fn template that can store/use/work with the above generated static references to member functions... ...ok...enough for now...hope this sweat turns out useful ;) ps. didn't use/post patch files deliberately because the changes are too big... -- "That men do not learn very much from the lessons of history is the most important of all the lessons of history." Aldous Huxley

Domagoj Saric wrote:
"OvermindDL1" <overminddl1@gmail.com> wrote in message news:3f49a9f40910021215g981da6cxd93c43ac52fee5cc@mail.gmail.com...
That is something I have been curious about since I first looked at the Boost.Function source. I was curious why it did not use any real policies like most other things in boost
it seems to me rather that there is no "clear cut" policy (pardon the pun :) about that (the policy approach) in boost...for example the shared_ptr
Of course not. It is up to each library developer and depends upon the purpose and audience of the library besides.
documentation states that shared_ptr uses a more joe sixpack approach with a "forced" dynamic deleter on purpose, to achieve a simpler interface and a more 'stable' abi (the point seems moot imho, the same could be achieved with default template parameters while retaining compile-time configurability)...
The same could not be achieved with defaulted template arguments. The idea is that, regardless of how something is allocated and deleted, the shared_ptr type only depends upon the type it points to. That permits interoperability among a great deal more code such as in Windows when a DLL uses a different RTL than the application.
And I *love* the idea about using a nop static func that throws, or perhaps asserts, all depending on policy settings for example. :)
well...here it is..."and more" ;-)
the boost::function documentation states that virtual functions are not used because they cause code bloat but comparing the actual binaries and code generated i found boost::function to actually be worse in that respect than the std::tr1::function implementation provided with msvc++ 9.0 SP1 (that does use virtual functions)...
The claim was made years ago and is for a library that doesn't have the benefit of being written by those with direct access to those writing the compiler. It may be that things have changed and that another approach is superior now, as your analysis of std::tr1::function in MSVC 9 SP1 suggests. _____ Rob Stewart robert.stewart@sig.com Software Engineer, Core Software using std::disclaimer; Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

"Stewart, Robert" <Robert.Stewart@sig.com> wrote in message news:DF2E67F3D097004694C8428C70A3FD69046F261514@msgbal516.ds.susq.com...
Domagoj Saric wrote:
documentation states that shared_ptr uses a more joe sixpack approach with a "forced" dynamic deleter on purpose, to achieve a simpler interface and a more 'stable' abi (the point seems moot imho, the same could be achieved with default template parameters while retaining compile-time configurability)...
The same could not be achieved with defaulted template arguments. The idea is that, regardless of how something is allocated and deleted, the shared_ptr type only depends upon the type it points to. That permits interoperability among a great deal more code such as in Windows when a DLL uses a different RTL than the application.
i realize that but can't the same be achieved with a template-parameter-specified deleter that is in fact a dynamic deleter? the default provided dynamic_deleter class could still be constructible from any source that makes the expression d(p) well formed (as stated by shared_ptr documentation)...just like the current boost::detail::sp_counted_impl_pd<> class is... in other words the status quo is as if you hard-coded the deleter template parameter to be a dynamic deleter... what i'd like is (like in all things) to have the option of using the power (you mentioned in your example) of a more stable/type only dependent ABI _when i actually need it_ and also being free of virtual functions/indirect calls (and maybe/possibly even rtti) when i do not need it (which "i bet" is the vast majority of shared_ptr<> uses "out there")... but, yes, this is going off topic...
the boost::function documentation states that virtual functions are not used because they cause code bloat but comparing the actual binaries and code generated i found boost::function to actually be worse in that respect than the std::tr1::function implementation provided with msvc++ 9.0 SP1 (that does use virtual functions)...
The claim was made years ago and is for a library that doesn't have the benefit of being written by those with direct access to those writing the compiler. It may be that things have changed and that another approach is superior now, as your analysis of std::tr1::function in MSVC 9 SP1 suggests.
well the stl shipped with msvc (9) is provided by dinkumware, not ms, and can be bought separately from the msvc++ compiler...and as such does not use some particular 'msvc++ specific' tricks...not that i know of...it certainly does not use any in the implementation of std::tr1::function<>...it is standard c++... the issue is what makes virtual functions different from plain function pointers and manual vtables in this case...they both constitute a 'brick wall' for the optimizing compiler and add indirection...virtual functions/standard polymorphism only adds rtti...but the compiler is free (afaik) not to include that rtti information/record in the final binary if it is not used (or rtti is disabled alltogether)...what constitutes a bigger problem is that the type information for the contained function object is forced to be included by the target<>() member function that is part of the interface...but that is inherent to the interface (and thus present in all compliant implementations...meaning in boost and dinkumware)... ...there are however many other implementation details that can be done differently (or be made subject to configurability) while retaining standard compliancy...and this is what i tried to do...and according to performed tests the result is better than both dinkumware and the current boost implementation in both space and time efficiency with added configuration power... ;) -- "That men do not learn very much from the lessons of history is the most important of all the lessons of history." Aldous Huxley

Domagoj Saric wrote:
"Stewart, Robert" <Robert.Stewart@sig.com> wrote in message news:DF2E67F3D097004694C8428C70A3FD69046F261514@msgbal516.ds.s usq.com...
Domagoj Saric wrote:
documentation states that shared_ptr uses a more joe sixpack approach with a "forced" dynamic deleter on purpose, to achieve a simpler interface and a more 'stable' abi (the point seems moot imho, the same could be achieved with default template parameters while retaining compile-time configurability)...
The same could not be achieved with defaulted template arguments. The idea is that, regardless of how something is allocated and deleted, the shared_ptr type only depends upon the type it points to.
i realize that but can't the same be achieved with a template-parameter-specified deleter that is in fact a dynamic deleter? the
As soon as you specialize the class template with anything other than the default deleter type, you have a different type. shared_ptr<T> is shared_ptr<T>, even if they use different deleters. your_ptr<T> isn't your_ptr<T,other_deleter>.
what i'd like is (like in all things) to have the option of using the power (you mentioned in your example) of a more stable/type only dependent ABI _when i actually need it_ and also being free of virtual functions/indirect calls (and maybe/possibly even rtti) when i do not need it (which "i bet" is the vast majority of shared_ptr<> uses "out there")...
What you want is something other than shared_ptr. It doesn't allow that flexibility, so shared_ptr's are, by design, interoperable.
the boost::function documentation states that virtual functions are not used because they cause code bloat but comparing the actual binaries and code generated i found boost::function to actually be worse in that respect than the std::tr1::function implementation provided with msvc++ 9.0 SP1 (that does use virtual functions)...
The claim was made years ago and is for a library that doesn't have the benefit of being written by those with direct access to those writing the compiler. It may be that things have changed and that another approach is superior now, as your analysis of std::tr1::function in MSVC 9 SP1 suggests.
well the stl shipped with msvc (9) is provided by dinkumware, not ms, and can be bought separately from the msvc++ compiler...and as such does not use some particular 'msvc++ specific' tricks...not that i know of
I wouldn't be surprised but what they had pretty easy access to those inside MS given that they were providing the STL for MS at that time. Still, I merely suggested a possibility. Peter Dimov must defend that design decision, if he cares to.
...there are however many other implementation details that can be done differently (or be made subject to configurability) while retaining standard compliancy...and this is what i tried to do...and according to performed tests the result is better than both dinkumware and the current boost implementation in both space and time efficiency with added configuration power... ;)
Those are great so long as they don't break with Peter's design ideals for the class. Otherwise, you optimize it into being a different class template. _____ Rob Stewart robert.stewart@sig.com Software Engineer, Core Software using std::disclaimer; Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

"Stewart, Robert" <Robert.Stewart@sig.com> wrote in message news:DF2E67F3D097004694C8428C70A3FD69046F55F9C0@msgbal516.ds.susq.com...
As soon as you specialize the class template with anything other than the default deleter type, you have a different type. shared_ptr<T> is shared_ptr<T>, even if they use different deleters. your_ptr<T> isn't your_ptr<T,other_deleter>.
yes it is, if "other_deleter" is in fact the default "dynamic_deleter" class that is again in fact only a wrapper around an actual dynamc deleter (>just like the current boost::detail::sp_counted_impl_pd<> class is< as said in my previous post)... so if we have template <typename T, class Deleter = dynamic_deleter> class my_ptr {...} then my_ptr<std::string> onePtr( someStringCreatorFunction(), &approprateStringDeleterFunction ); my_ptr<std::string> otherPtr( new std::string( "str" ) ); //will use the default deleter/plain delete ... otherPtr = onePtr; ... compiles and works just as current shared_ptr<> does...with the added option of disabling dynamic deleter functionality _when_ it is not needed.... so simply...the code that needs cross module interoperability simply uses the default dynamic_deleter class or explicitly specifies it if it wants to make it more clear...and in effect, in that case, you also get the same "shared_ptr<> type depends only on the type pointed to" behaviour (because everyone is using the same deleter...not _actually_ same but of the same, wrapping, type/signature)... in otherwords you can always go from static to dynamic...but not vice verse... to make it more on topic...i, think i, proved this true exactly with the changes i made to boost::function<>: even with an added policy parameter (properly defaulted) existing code compiles and behaves exactly as before...
Peter Dimov must defend that design decision, if he cares to.
actually not, that was not my point, that decision was perfectly correct... ...my point was just that, even in spite of that correct decision, the goal of "creating less bloat" was not achieved (because of other issues outlined in the first post)...
Those are great so long as they don't break with Peter's design ideals for the class. Otherwise, you optimize it into being a different class template.
well there's something from both bowls :) part/most of the changes do not need/rely on the changed template signature while others do (the addition of policies)... -- "That men do not learn very much from the lessons of history is the most important of all the lessons of history." Aldous Huxley

Domagoj Saric wrote:
"Stewart, Robert" <Robert.Stewart@sig.com> wrote
As soon as you specialize the class template with anything other than the default deleter type, you have a different type. shared_ptr<T> is shared_ptr<T>, even if they use different deleters. your_ptr<T> isn't your_ptr<T,other_deleter>.
yes it is, if "other_deleter" is in fact the default "dynamic_deleter" class that is again in fact only a wrapper around an actual dynamc deleter (>just like the current boost::detail::sp_counted_impl_pd<> class is< as said in my previous post)...
That's a big "if." The point is, your_ptr<T> is not the same as shared_ptr<T>. If you want a class with a potentially different deleter type, create one, document it, and submit it to Boost. The value of shared_ptr<T> is that all shared_ptr<T>'s have the same deleter type, so they are always compatible. Perhaps Peter would be willing to implement shared_ptr<T> as deriving from your_ptr<T,dynamic_deleter> in order to share code, but shared_ptr<T> shouldn't be changed to allow parameterizing the deleter type. _____ Rob Stewart robert.stewart@sig.com Software Engineer, Core Software using std::disclaimer; Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

on Thu Oct 29 2009, "Stewart, Robert" <Robert.Stewart-AT-sig.com> wrote:
the boost::function documentation states that virtual functions are not used because they cause code bloat but comparing the actual binaries and code generated i found boost::function to actually be worse in that respect than the std::tr1::function implementation provided with msvc++ 9.0 SP1 (that does use virtual functions)...
The claim was made years ago and is for a library that doesn't have the benefit of being written by those with direct access to those writing the compiler. It may be that things have changed and that another approach is superior now, as your analysis of std::tr1::function in MSVC 9 SP1 suggests.
I'm very surprised to hear that... perhaps you used the default MSVC++ settings which IIRC don't include RTTI? -- Dave Abrahams Meet me at BoostCon: http://www.boostcon.com BoostPro Computing http://www.boostpro.com

"David Abrahams" <dave@boostpro.com> wrote in message news:m2ljild5s4.fsf@zreba.luannocracy.com...
I'm very surprised to hear that... perhaps you used the default MSVC++ settings which IIRC don't include RTTI?
RTTI defaults to "On" since msvc++ 8.0 ... ...and yes with rtti on boost::function<> still produced a larger binary... msvc++ tr1 function<> did add one or two rtti records more (if rtti is left enabled) but, as i elaborated in my first post, it turns out this is actually a less important issue (relatively speaking)...maybe it would become more important for projects/binaries with a large number of different function<> instantiations (comparing to the relatively few i tested with) because the much more complex/'bloated' boost::function<> 'throw bad_function_call' machinery (as compared to the same 'machinery' in mscv++ tr1 function<>) would become less important (as atleast some parts of it are 'allocated' only once/not part of the template)...but the template bloat issues i outlined in my first post would still prevail significantly (over a larger number of rtti records, which can be turned off if you do not use rtti otherwise)... ps. i don't know, is a topic name change in order? like a request for a mini review or something... -- "That men do not learn very much from the lessons of history is the most important of all the lessons of history." Aldous Huxley

"Domagoj Saric" <dsaritz@gmail.com> wrote in message news:hcagtr$mog$1@ger.gmane.org...
...
... ok ... the fourth attempt in two days ... now i'll try without an attachment (rar and tgz did not pass) and put the source in the boostpro vault ( home / function objects / new_function_implementation_proposal.tgz ) ...
...
khm...could someone please tell me what could have been the problem (for future 'avoidance')...it seems that something filtered my posts that had .rar or .tgz attachements... http://www.boost.org/community/policy.html#max-size says that messages up to 75kB are allowed (mine were less than that)... -- "That men do not learn very much from the lessons of history is the most important of all the lessons of history." Aldous Huxley

on Sun Nov 01 2009, "Domagoj Saric" <dsaritz-AT-gmail.com> wrote:
khm...could someone please tell me what could have been the problem (for future 'avoidance')...it seems that something filtered my posts that had .rar or .tgz attachements... http://www.boost.org/community/policy.html#max-size says that messages up to 75kB are allowed (mine were less than that)...
As far as I can tell, the only mailing list settings related to attachments cause files with these extensions to be stripped: exe bat cmd com pif scr vbs cpl Maybe our generous hosts at IU have some additional filters outside Mailman? -- Dave Abrahams Meet me at BoostCon: http://www.boostcon.com BoostPro Computing http://www.boostpro.com

"David Abrahams" <dave@boostpro.com> wrote in message news:m2fx8td5mk.fsf@zreba.luannocracy.com...
As far as I can tell, the only mailing list settings related to attachments cause files with these extensions to be stripped:
exe bat cmd com pif scr vbs cpl
Maybe our generous hosts at IU have some additional filters outside Mailman?
i don't know...but i did try certainly more than ~4 times during the course of two days...and it appeared on the list only once i removed the attachment (i recieved no 'bounce'-like reply)...but thankfully the boost vault has free public upload access so that solved (or circumvented) the problem ;) -- "That men do not learn very much from the lessons of history is the most important of all the lessons of history." Aldous Huxley

On Wed, Oct 28, 2009 at 5:34 PM, Domagoj Saric <dsaritz@gmail.com> wrote:
And I *love* the idea about using a nop static func that throws, or perhaps asserts, all depending on policy settings for example. :)
well...here it is..."and more" ;-)
[...snip...] I'd love to see some of these improvements to boost::function. In particular, I'd like to be able to configure the size of the small buffer optimization. Of course, I'd also like it to still be compatible with function<>s of other sizes. ie boost::function<void (int), 16 > func16 = ...; boost::function<void (int), 12 > func12; func12 = func16; // need this to still work (ie operator= needs to be templatized on sbo size) Some of the other changes looked interesting as well. I do understand the rational of a single, simple shared_ptr<>, and that this might also apply to function<>. But I would think that templatized copy constructors, etc, could make all the boost::functions compatible with each other, regardless of policy. I think... Tony

Domagoj Saric wrote:
As far as I can see boost::function<>::operator() performs: if (this->empty()) boost::throw_exception(bad_function_call()); before invoking the stored function (object)... My question is why or why only this "checked"/"safe" approach/method of invocation exists?
I think this branching can be effectively elided without sacrificing safety if boost::function maintains never-empty internal guarantee. IOW, if function::empty() returns true, then the function object is actually pointing to some private function that simply throws upon being invoked.

Andrey Semashev wrote:
I think this branching can be effectively elided without sacrificing safety if boost::function maintains never-empty internal guarantee. IOW, if function::empty() returns true, then the function object is actually pointing to some private function that simply throws upon being invoked.
That looks like a very good idea.

"Andrey Semashev" <andrey.semashev@gmail.com> wrote in message news:4AC62580.9050801@gmail.com...
I think this branching can be effectively elided without sacrificing safety if boost::function maintains never-empty internal guarantee. IOW, if function::empty() returns true, then the function object is actually pointing to some private function that simply throws upon being invoked.
:) hello noosphere ;-) -- "That men do not learn very much from the lessons of history is the most important of all the lessons of history." Aldous Huxley
participants (8)
-
Andrey Semashev
-
David Abrahams
-
Domagoj Saric
-
Domagoj Saric
-
Gottlob Frege
-
Mathias Gaunard
-
OvermindDL1
-
Stewart, Robert