0

FIRST AND FOREMOST: Forgive me if the following question falls into the "off topic" category of this StackExchange, due to its slightly opinion polling nature. This is not my intention, I am simply looking for good practice advice in regards to general API design.

I am current implementing an applications development framework.

Within the framework API there are clients and api services.. A client is an object created by the user, and an api service is an object provided by the framework. Each object must be able to call the other.

Now currently I see two possible ways of doing this when regarding the complete problem domain:

SOLUTION 1: Each client/service object must implement a FunctionTable object as a member variable and contain all required init code much like the following:

struct MyClient {

    MyClient(ServiceProxy& service) {

        functionTable.init(this, {"foo", "bar"});

        service.registerClientContract(&functionTable);
    }

    void foo(int a, float b) {

    }

    void bar() {

    }

    struct FunctionTable<
                        MyClient,
                        decltype(&MyClient::foo),
                        decltype(&MyClient::bar)
                    > functionTable;
}

SOLUTION 1 PROBLEM: This method leads to a lot of gross boiler plate code on both the client and API side. This leads me to the second solution.

SOLUTION 2: The client simply registers its functions directly with the service, and the service dynamically creates the table internally on the heap. Example:

struct MyClient {

    MyClient(ServiceProxy& service) {

        service.registerClientContract
        (
            MyClient
            std::make_pair("foo", decltype(&MyClient::foo)),
            std::make_pair("bar", decltype(&MyClient::bar))
        );

    }
}

SOLUTION 2 PROBLEM: Slightly less SOLID friendly?

THE QUESTION: Could solution 1 become a headache for the user of the API. Would an experienced API designer tend towards the encapsulation benefits of solution 2.

Thanks :)

  • If anyone has an answer that does not involve the creation of interfaces that would be awesome! I am experimenting with some features which require a high level of dynamic runtime behavior, thus the function table is required for now. – NJ Mercaldo Aug 23 '18 at 17:51
  • 1
    Another option might be to use the command pattern, which is just an object oriented call-back. – Greg Burghardt Aug 23 '18 at 20:32
0

One word: Interfaces.

Ok, since this is C++ 10 words: abstract classes with only pure abstract methods and multiple inheritance.

The operations these two different types should perform on each other should be defined in the "interface". Then just pass each object around, and each object defines methods that take the other objects as their interface type rather than concrete type.

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.