Question

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 :)

Was it helpful?

Solution

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.

Licensed under: CC-BY-SA with attribution
scroll top