Using the C-like API in OOP

I am writing some classes in C ++ to act as a personal little library and I am facing a problem.

Some of my objects make us from third party libraries written in great C style, which means that these libraries have functions like apiInit()

and apiCleanup()

where the former should be called before any of the actual api functions and the latter should be called when you are no longer going to use them.

I want to provide classes that need a library with an access point to its functions, ensuring that it apiInit()

is called when the first required class is created, or at least before any api function is used, and apiCleanup()

called when the last instance that the api is using is destroyed.

Be aware that there is more than one class that uses the same library.

I could come up with two solutions:

First, the obvious one, make the provider single:

#include <iostream>

using namespace std;


class ContextProvider {

  ContextProvider() {
    cout << "Initializing API" << endl;
  }

  ContextProvider(ContextProvider const& rhs) = delete;
  ContextProvider& operator=(ContextProvider const& rhs) = delete;

public:

  ~ContextProvider() {
    cout << "Cleaning API" << endl;
  }

  static ContextProvider& getInstance() {
    static ContextProvider instance;
    return instance;
  }

  void useContext() {
    cout << "Using API" << endl;
  }
};


class ContextUser1 {


public:

  ContextUser1() {

  }

  void doSomething() {
    ContextProvider::getInstance().useContext();
  }
};


class ContextUser2 {

public:

  ContextUser2() {

  }

  void doSomethingElse() {
    ContextProvider::getInstance().useContext();
  }
};

      

Another is to keep a count of contextual users, for example:

#include <iostream>

using namespace std;


class ContextProvider {
  static unsigned int userCounter;

public:

  ContextProvider() {
    if (userCounter == 0)
      cout << "Initializing API" << endl;

    ++userCounter;
  }

  ~ContextProvider() {
    --userCounter;

    if (userCounter == 0)
      cout << "Cleaning API" << endl;
  }

  void useContext() {
    cout << "Using API" << endl;
  }
};

unsigned int ContextProvider::userCounter = 0;


class ContextUser1 {
  ContextProvider cp;

public:

  ContextUser1() {
    cp = ContextProvider();
  }

  void doSomething() {
    cp.useContext();
  }
};


class ContextUser2 {
  ContextProvider cp;

public:

  ContextUser2() {
    cp = ContextProvider();
  }

  void doSomethingElse() {
    cp.useContext();
  }
};


int main() {
  ContextUser1 cu11, cu12, cu13;
  ContextUser2 cu21, cu22;

  cu11.doSomething();
  cu12.doSomething();
  cu21.doSomethingElse();
  cu22.doSomethingElse();
  cu13.doSomething();
}

      

Both when executed with the following main()

int main() {
  ContextUser1 cu11, cu12, cu13;
  ContextUser2 cu21, cu22;

  cu11.doSomething();
  cu12.doSomething();
  cu21.doSomethingElse();
  cu22.doSomethingElse();
  cu13.doSomething();
}

      

will delay the expected result, that is:

Initializing API
Using API
Using API
Using API
Using API
Using API
Cleaning API

      

Now the obvious question is , which one is better, or which one should I use?

For example, some things that come to mind are ...

Singleton method:

  • Benefits:

    • No need to store counter.
    • No need to store any instance.
  • Disadvantages:

    • The syntax gets weird ( ContextProvider::getInstance().use()

      ).
    • It is singleton, with all its disadvantages.

Counter method:

  • Benefits:

    • The use is straightforward.
    • The syntax is nice and straightforward ( cp.use()

      ).
  • Disadvantages:

    • A user counter must be maintained.
    • Custom classes must store an instance of the ContextProvider class.

Basically I ask this question because I do not know which of these advantages / disadvantages the weight is greater, if there are things that I did not take into account, or maybe there is an obvious third method, I could not come up with which in essence better than these two, or who knows.

Thank you for your time!

+3


source to share


1 answer


I would use your second approach with the following changes:

class ContextUser1 {
  std::shared_ptr<ContextProvider> cp;

public:

  ContextUser1(const std::shared_ptr<ContextProvider>& cp)
      : cp(cp) {
  }

  void doSomething() {
      cp->useContext();
  }
};

      



Making the dependency explicit makes your code better for validation. Plus, the usage shared_ptr

does the accounting, so you don't even need to do it yourself.

+1


source







All Articles