4

I'm building a smallish library (few kLOC) which processes stream data in C++. From the streamed data (comes in packets) the library compiles a database piece by piece and naturally has to convey that information back to the host application.

Since this is my first library I'm unsure how to proceed with the API/Interface. AFAIK I have the choice between using a CPP "Interface" or C-style callbacks. This question is not just related to the programming aspect but also to the aspect of managing the library API between versions:

Why would I use an Interface?

  • Interfaces are declared once and API changes are directly reflected in that class
  • It is impossible to forget a method or an event as the library just doesn't compile
  • It is "cleaner" C++ than C-style Callbacks
  • I'm sure what I'm doing within the library and, hide "maybe complex" stuff behind a return value

Using an Interface

class InterfaceClass {

public: 

    virtual bool isDataBlockComplete(...) = 0;
    virtual bool isDataBaseUpdated(...) = 0;
    virtual bool hasEventCompleted(...) = 0;
    [..]
}


/* User code */

class SomeClass : InterfaceClass { [..] }

bool SomeClass::hasEventCompleted(...){ /* check */ }

SomeClass *instance = new SomeClass();

InterfaceClass *instance = new InterfaceClass();

if(instance->hasEventCompleted) [..]

for(i = 0; i < instance->getQueueSize(); i++ ) [..]

Why would I use C-style Callbacks?

  • They can be registered and updated during runtime
  • Events can be "subscribed" this way
  • Helps in providing a C-Wrapper later on
  • It's way faster because less code has to be written & tested by me

Using Callbacks

class InterfaceClass {

public:

    registerCallback( void *f, tEvent event);
    deregisterCallback(tEvent event);
}


/* User code */

void myfunc(){ .. }

instance->registerCallback(&myfunc, SOME_EVENT);

/* Gets called by the governor / observer of library */

So this is not a question of preferability, but which "style" to be used for a library. I didn't find anything on the net (it all seems to revolve around the technical aspects of using virtual abstract classes or on how to implement callbacks but no one actually discusses what I've asked above) despite a thorough search (maybe wrong choice of word combinations).

Which style is better suited for such kind of library and why? How do "professional" libraries communicate with the main applications? (Maybe too broad for SE, but trying anyway :))

  • I know C++ only barely so i'll comment instead of answering. AFAICT you can implement Pub/Sub (callbacks) with interfaces as well. Thats the way things work in the Java, C# and JS universes. EDIT: some examples: developer.android.com/reference/android/view/… docs.oracle.com/javase/8/docs/api/java/awt/event/… – marstato Dec 15 '16 at 11:13
  • I don't understand: why test if (instance->hasEventCompleted)? There can be no instance that hasn't overridden the abstract method, no? – Erik Eidt Dec 15 '16 at 15:41
  • I completed the example now. I have been thinking a little bit too long about it and thought my example code would be fine but in fact I was thinking too far. – Jan Krüger Dec 15 '16 at 16:44
  • I completed the example now. I have been thinking a little bit too long about it and thought my example code would be fine but in fact I was thinking too far. I don't know a lot about Java, but I'm not aware of any C++11 features or any libraries which would allow for a similar functionality to Java. – Jan Krüger Dec 15 '16 at 16:47
1

I would suggest avoiding the use of C-style callbacks in C++ code. C++ provides an equivalent idiom that gives better type-safety, is easier to read (at least for most C++ developers), and may provide the compiler better chances to optimize the code. It also allows the use of lambda functions to define the behaviour at call site, which you can't achieve using function pointers. This is the std::function template class in the <functional> header.

Using this, your code might look like:

class InterfaceClass {

public:
   registerCallback(std::function<void(MyEvent)> callback, tEvent event);
   deregisterCallback(tEvent event);
};

instance->registerCallback ([] (MyEvent evt) { 
                               /* do something here */ 
                            }, SOME_EVENT);
3

I suggest you to consider observer pattern. You can register and unregister event listeners (observers) at runtime.

If you want use a legacy C function as callback, you create a wrapper observer class which delegate event notifications to that callback.

  • Thanks! The observer pattern was exactly what I needed. Coming from electrical engineering I never actually learned how to design good software. It's a shame, really :) – Jan Krüger Dec 16 '16 at 8:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.