0

I have noticed that many libraries (e.g. Box2D, Bullet) use user-pointer e.g. void*.

However, in some cases, I believe using value is faster/better.

  • avoid heap allocation / better memory locality
  • don't need to delete it manually, or use smart pointer
  • sometimes, it just simply looks better
  • when user don't set value of userData (leave as default), the program will not crash

What are disadvantage using value?

More Solid Example

I have a performance-critical complex database ComplexDB that has callback.

Whenever it add/remove new elements by any means, it will callback.

template<class T,class Callback> class ComplexDB{
    Callback call;  //<-- will be Callback* in pointer-version
    void addElement(const T& t){
        Callback::callbackAdd(call,t);  //<-- callback
    }
    // other complex functions that may call "addElement()"
};
class MyCallback{
    int custom=5;
    public: static void callbackAdd(const MyCallback& c,int a){
        int cus=c.custom; 
        //^ It is cheaper than pointer-version, 
        //     because c's address has memory-locality near "ComplexDB".
    }
    //other function e.g. callbackRemove(), and a few other callbacks
}

It works good.

Nonetheless, I am very new to C++, so I am afraid that I am violating some C++ holy tradition, and I will be punished e.g. cause maintainability problem / broken design.

I have doubted about this for a week, but feel very reluctant to ask it.
If this question should be improved in some ways, please comment. Thank.

Here is the most related question, but it focus on approaches that require v-table look-up or std::function that I can't afford.

Edit: The ComplexDB expects that a callback class to contain specific 3-5 functions.
(e.g. callbackAdd,callbackRemove,...)
Thus, I think it is suitable to pack it as only 1 template-argument slot.

Community
  • 1
  • 1
javaLover
  • 6,347
  • 2
  • 22
  • 67
  • 1
    I'd recommend not to accept data at all. Accept a general functor type (i.e. anything that can have `()` applied to it), that you then copy. Let the users worry about data by passing a capturing lambda or calling `std::bind` or whatever. – StoryTeller - Unslander Monica May 13 '17 at 05:50
  • 1
    In C, you should pass through an `intptr_t`. The caller can use it either as a value or as a pointer, at their choice. In C++, as StoryTeller says you should support state via type-safe objects and not require a raw pointer (which then has to be manually freed) – Ben Voigt May 13 '17 at 06:08
  • @StoryTeller I can't use `std::bind` because it is too slow (profiled). I don't think lambda is appropriate because I expect a Callback class that has many functions (sorry, I didn't mention, edited). If I pass lambda, there would be many template parameters (dirty), and I will not get some non-static field in userData (e.g. `MyCallback::custom`). – javaLover May 13 '17 at 06:32
  • @StoryTeller If there is no userData, I will have to pass `lambda[&]` whenever I call operation like `ComplexDB::add/remove(lambda)`. It is not so convenient. – javaLover May 13 '17 at 06:39
  • @javaLover - Convenience is a matter of perspective. Some would say that it's more convenient to have the functor manage the lifetime of the data, rather than have to do it separately on account of that data being seperate (as Ben pointed out). – StoryTeller - Unslander Monica May 13 '17 at 07:02
  • @javaLover - Also, those functions need not be static. You should write the class with non static member functions, IMO. I.e `call.add(t);` – StoryTeller - Unslander Monica May 13 '17 at 07:04
  • @StoryTeller I believe using static function is cheaper (performance-wise) in situation that userPointer is not really need i.e. algorithm of callback doesn't depend on instance, e.g. empty callback. After optimization (become inline), it will cost nothing. – javaLover May 13 '17 at 07:05
  • 1
    @javaLover - That's a premature optimization really. It's a function call either way. There's no significant cost involved with passing the callback as `this`, as opposed to your scheme of `const MyCallback& c`. The compiler is just as good at inlining non-static members. Consider the case of the standard library, where `operator()` is always a non-static member. The code is inlined just fine. – StoryTeller - Unslander Monica May 13 '17 at 07:09
  • @StoryTeller Agree - it is quite premature. There is another advantage of value - when a user is too lazy to set value of `Callback* call` (i.e. want to leave as default), `call` will be `nullptr`, and any `call->` will crash. It will not be the case if it is value. – javaLover May 13 '17 at 07:12
  • @javaLover - True. A value ensures your code is correct by construction (and so should be the default approach before bench-marking). – StoryTeller - Unslander Monica May 13 '17 at 07:18

0 Answers0