8

For my library, I want to expose a clean public API that does not distract with implementation details. As you have it, though, these details are leaking even to the public realm: Some classes have valid public methods that are used by the rest of the library, but aren't very useful for the user of the API and as such don't need to be a part of it. A simplified example of the public code:

class Cookie;

class CookieJar {
public:
    Cookie getCookie();
}

class CookieMonster {
public:
    void feed(CookieJar cookieJar) {
        while (isHungry()) {
            cookieJar.getCookie();
        }
    }

    bool isHungry();
}

The getCookie() method of a CookieJar is not useful to the user of the library, who presumably does not like cookies anyway. It is, however, used by the CookieMonster to feed itself, when given one.

There are some idioms that help solve this issue. The Pimpl idiom offers to hide the private members of a class, but does little to disguise the public methods that are not supposed to be a part of the API. It is possible to move those into the implementation class as well, but you would then need to provide direct access to it for the rest of the library to use. Such a header would look like this:

class Cookie;
class CookieJarImpl;

class CookieJar {
public:
    CookieJarImpl* getImplementation() {
        return pimpl.get();
    }
private:
    std::unique_ptr<CookieJarImpl> pimpl;
}

It is handy if you really need to prevent user access to these methods, but if it's merely an annoyance, this doesn't help very much. In fact, new the method is now even more useless than the last, because the user does not have access to the implementation of CookieJarImpl.

An alternative approach is to define the interface as an abstract base class. This gives explicit control over what is a part of the public API. Any private details can be included in the implementation of this interface, which is inaccessible to the user. The caveat is that the resulting virtual calls impact performance, even more so than the Pimpl idiom. Trading speed for a cleaner API is not very attractive for what is supposed to be a high performance library.

To be exhaustive, yet another option is to make the problematic methods private and use friend classes where needed to access them from the outside. This, however, gives the target objects access to the truly private members as well, somewhat breaking encapsulation.

So far the best solution to me seems to be the Python way: Instead of trying to hide the implementation details, just name them appropriately, so that they are easily identifiable as not part of the public API and do not distract from regular usage. The naming convention that comes to mind is using an underscore prefix, but apparently such names are reserved for the compiler and their use is discouraged.

Are there any other c++ naming conventions for distinguishing members that are not intended to be used from outside the library? Or would you instead suggest me to use one of the alternatives above or something else I missed?

Quinchilion
  • 912
  • 6
  • 16
  • I think you make the methods you want hidden from the end user public, make the rest private, and for the now-private ones that the rest of your API needs, just declare those classes to be a friend of your class here? https://msdn.microsoft.com/en-us/library/465sdshe.aspx – Cody Aug 09 '16 at 16:27
  • 2
    May be this design approach helps: http://stackoverflow.com/questions/27492132/how-can-i-remove-refactor-a-friend-dependency-declaration-properly – πάντα ῥεῖ Aug 09 '16 at 16:29
  • @Cody I did consider that option in my question. The two classes are not nearly related enough to make using friend appropriate here... at least according to many style guides I've seen. – Quinchilion Aug 09 '16 at 17:12
  • There's another option: Put implementation specifics in a different namespace. – Ivan Rubinson Aug 09 '16 at 17:28
  • @IvanRubinson How? You can't split the definition of a class as far as I'm aware. – Quinchilion Aug 09 '16 at 17:31
  • @Quinchilion : Your question exposes a really big problem I faced so many times for years... without any efficient solution yet. (By the way, I use friendship in this situation, which indeed is not ideal.) The design proposed by πάντα ῥεῖ seams to be a quite elegant solution to the problem though a bit complex to implement... – shrike Aug 09 '16 at 17:32
  • @πάνταῥεῖ I talked about the option of using an explicit abstract interface to define the API as well. It is a valid solution. A good solution in most cases. However, I'm worried about the performance of turning most of the calls the user will make into the rather expensive virtual calls. I'd much rather just name the methods in question differently, to distinguish them from the API. After all, it's not an issue if user calls them - it won't break anything. They are simply not useful and can be confusing. – Quinchilion Aug 09 '16 at 17:38
  • 2
    @Quinchilion It's a common myth that virtual function call are _expensive_.. Also you could use a CRTP for implementation and use static polymorphism, if you really need to worry about this. – πάντα ῥεῖ Aug 09 '16 at 17:42
  • @πάνταῥεῖ Well, as I said, this is high performance code. Even if the overhead ends up not being significant, a heavy use will raise some questions. I looked into CRTP. It looks promising and not too difficult to implement. One thing I'm worried about, though: Won't the templates leak into the public API? That is, will the user need to use templated functions to work with the typed interface? I don't want to complicate an already complex API with this. – Quinchilion Aug 09 '16 at 18:21
  • @Quinchilion _"Won't the templates leak into the public API? "_ Well, the whole point of my approach is to inherit that interface as `protected`, same for static polymorphism. – πάντα ῥεῖ Aug 09 '16 at 18:24
  • @πάνταῥεῖ Of course. I'm wondering what impact this would have on the API, though. Presumably a factory pattern will have to be used instead of a constructor and the returned type would be something like `CookieJarInterface` though that can be hidden with typedef. I would appreciate if you provided an answer with example code. As long as the it doesn't end up too complex for the user, I'll select it as the answer. – Quinchilion Aug 09 '16 at 18:40
  • @Quinchilion Your question makes it a bit hard to give you a definite answer at Stack Overflow (too broad, opinion based). IMO such question might be better asked at [SE Programmers](http://programmers.stackexchange.com/). – πάντα ῥεῖ Aug 09 '16 at 18:43
  • 1
    @πάνταῥεῖ when referring other sites, it is often helpful to point that [cross-posting is frowned upon](http://meta.stackexchange.com/tags/cross-posting/info) – gnat Aug 09 '16 at 18:51
  • @πάνταῥεῖ You already gave me a solid answer I'm willing to accept. I agree the question name sounds somewhat broad, but in the end it presents a specific problem with only a few solutions. – Quinchilion Aug 09 '16 at 18:53
  • @Quinchilion Well, it would need some more efforts from my side to write a really good answer here scribbling up all of my thoughts in a good piece of code. I can't promise I'll do that soon. Feel free to write your own answer based on all that. I'll probably upvote it ;) – πάντα ῥεῖ Aug 09 '16 at 18:58
  • @πάνταῥεῖ : "It's a common myth that virtual function call are expensive" Not a myth, a fact. It really depends on how often you call the function. Benchmarked on my computer, a direct function call needs about 0.5ns, while a virtual function call needs 2ns. So, yes, 2ns is fast but still it's 4 times slower than the direct call. – shrike Aug 10 '16 at 08:29

6 Answers6

3

Answering my own question: This idea is based on the interface - implementation relationship, where the public API is explicitly defined as the interface, while the implementation details reside in a separate class extending it, inaccessible to the user, but accessible to the rest of the library.

Halfway through implementing static polymorphism using CRTP as πάντα ῥεῖ suggested to avoid virtual call overhead, I realized polymorphism is not actually needed at all for this kind of design, as long as only one type will ever implement the interface. That makes any kind of dynamic dispatch pointless. In practice, this means flattening all the ugly templates you get from static polymorphism and ending up with something very simple. No friends, no templates, (almost) no virtual calls. Let's apply it to the example above:

Here is the header, containing just the public API with example usage:

class CookieJar {
public:
    static std::unique_ptr<CookieJar> Create(unsigned capacity);

    bool isEmpty();
    void fill();

    virtual ~CookieJar() = 0 {};
};

class CookieMonster {
public:
    void feed(CookieJar* cookieJar);
    bool isHungry();
};

void main() {
    std::unique_ptr<CookieJar> jar = CookieJar::Create(20);
    jar->fill();
    CookieMonster monster;
    monster.feed(jar.get());
}

The only change here is turning CookieJar into an abstract class and using a factory pattern instead of a constructor.

The implementations:

struct Cookie {
    const bool isYummy = true;
};

class CookieJarImpl : public CookieJar {
public:
    CookieJarImpl(unsigned capacity) :
        capacity(capacity) {}

    bool isEmpty() {
        return count == 0;
    }

    void fill() {
        count = capacity;
    }

    Cookie getCookie() {
        if (!isEmpty()) {
            count--;
            return Cookie();
        } else {
            throw std::exception("Where did all the cookies go?");
        }
    }

private:
    const unsigned capacity;
    unsigned count = 0;
};

// CookieJar implementation - simple wrapper functions replacing dynamic dispatch
std::unique_ptr<CookieJar> CookieJar::Create(unsigned capacity) {
    return std::make_unique<CookieJarImpl>(capacity);
}

bool CookieJar::isEmpty() {
    return static_cast<CookieJarImpl*>(this)->isEmpty();
}

void CookieJar::fill() {
    static_cast<CookieJarImpl*>(this)->fill();
}

// CookieMonster implementation
void CookieMonster::feed(CookieJar* cookieJar) {
    while (isHungry()) {
        static_cast<CookieJarImpl*>(cookieJar)->getCookie();
    }
}

bool CookieMonster::isHungry() {
    return true;
}

This seems like a solid solution overall. It forces using a factory pattern and if you need copying and moving, you need to define the wrappers yourself in a similar fashion to the above. That is acceptable for my use case, since the classes I needed to use this for are heavyweight resources anyway.

Another interesting thing I noticed is that if you feel really adventurous, you can replace static_casts with reinterpret_casts and as long as every method of the interface is a wrapper you define, including the destructor, you can safely assign any arbitrary object to an interface you define. Useful for making opaque wrappers and other shenanigans.

Quinchilion
  • 912
  • 6
  • 16
  • I'm still not sure whether this approach is better than simply naming the methods in a clear way, so that they cannot be confused with public API. I asked whether there is an existing naming convention for this, since using an underscore prefix is discouraged. Nobody has answered that question yet... – Quinchilion Aug 09 '16 at 22:32
  • Interesting design, though I do not catch how it really improves the pimpl solution you proposed in your question... also, is the definition of `struct Cookie` really needed in the public API side ? – shrike Aug 10 '16 at 09:15
  • @shrike There is no way to avoid having at least one unnecessary public method with the Pimpl design. Also, it moves the pointer dereference to user code, where a pointer is likely to be used anyway. And you're right. I forgot that `Cookie` is not a part of the API. It's not needed at all in the header. – Quinchilion Aug 10 '16 at 10:28
2

Consider the following code:

struct Cookie {};

struct CookieJarData {
    int count;
    int cost;
    bool whatever;
    Cookie cookie;
};

struct CookieJarInternal {
    CookieJarInternal(CookieJarData *d): data{d} {}
    Cookie getCookie() { return data->cookie; }
private:
    CookieJarData *data;
};

struct CookieJar {
    CookieJar(CookieJarData *d): data{d} {}
    int count() { return data->count; }
private:
    CookieJarData *data;
};

template<typename... T>
struct CookieJarTemplate: CookieJarData, T... {
    CookieJarTemplate(): CookieJarData{}, T(this)... {}
};

using CookieJarImpl = CookieJarTemplate<CookieJar, CookieJarInternal>;

class CookieMonster {
public:
    void feed(CookieJarInternal &cookieJar) {
        while (isHungry()) {
            cookieJar.getCookie();
        }
    }

    bool isHungry() {
        return false;
    }
};

void userMethod(CookieJar &cookieJar) {}

int main() {
    CookieJarImpl impl;
    CookieMonster monster;

    monster.feed(impl);
    userMethod(impl);
}

The basic idea is to create a class that is at the same time the data and derives from a bunch of subclasses.
Because of that, the class is its subclasses and you can use them whenever you want by choosing the right type. This way, the combining class has a full interface and is built up if a few components that share the same data, but you can easily return a reduced view of that class that still doesn't have virtual methods.

skypjack
  • 49,335
  • 19
  • 95
  • 187
  • I like the idea of using templates for a component system. The `CookieMonster` class is also a part of the public API, though. It's the user that would create a CookieJar and pass it to the CookieMonster. To do that, wouldn't you have to expose `CookieJarInternal` to the user, making all this kind of pointless? I apologize for not making it clearer. – Quinchilion Aug 09 '16 at 18:29
  • @Quinchilion Who will call `feed`? I'm to post another answer, but it mostly depends on this question. Let me know. – skypjack Aug 09 '16 at 19:16
  • Whoever the user of the file is. It can be called from anywhere. – Quinchilion Aug 09 '16 at 20:28
1

I have two ideas for this. In the first one, you create a CookieJarPrivate class to expose the private CookieJar methods to other parts of your library. CookieJarPrivate would be defined in a header file which does not form part of your public API. CookieJar would declare CookieJarPrivate to be its friend. It's technically not necessary for cookiejar.h to include cookiejarprivate.h, but doing so stops your clients trying to abuse the friend to gain access to implementation details by defining their own CookieJarPrivate.

class Cookie;

class CookieJarPrivate {
public:
    Cookie getCookie();
private:
    CookieJarPrivate(CookieJar& jar) : m_jar(jar) {}
    CookieJar& m_jar;
};

class CookieJar {
    friend class CookieJarPrivate;
public:
    CookieJarPrivate getPrivate() { return *this; }
private:
    Cookie getCookie();
};

class CookieMonster {
public:
    void feed(CookieJar cookieJar) {
        while (isHungry()) {
            cookieJar.getPrivate().getCookie();
        }
    }

    bool isHungry();
};

Cookie CookieJarPrivate::getCookie() {
    return m_jar.getCookie();
}

The compiler should be able to inline the CookieJarPrivate constructor and the getPrivate() method, so performance should be equivalent to a direct call to the private getCookie(). You might pay the penalty of one extra function call if the compiler elects not to inline the call to m_jar.getCookie() in the implementation of CookieJarPrivate::getCookie(). It could elect to do so, if both methods were defined in the same translation unit, especially if it could prove that the private getCookie() is not called anywhere else, but it's certainly not guaranteed.


The second idea is a dummy parameter of class type, with a private constructor and a friend relation on CookieMonster, so that the method can only be called by code which can construct this dummy type, i.e. only CookieMonster. This is like a normal friend but with finer granularity.

template <class T> class Restrict {
    friend T;
private:
    Restrict() {}
};

class Cookie;
class CookieMonster;

class CookieJar {
public:
    Cookie getCookie(Restrict<CookieMonster>);
};

class CookieMonster {
public:
    void feed(CookieJar cookieJar) {
        while (isHungry()) {
            cookieJar.getCookie({});
        }
    }

    bool isHungry();
};

A variation of this is a non-template dummy, with no friend, defined in a non-public header. It is still granular with respect to which methods are exposed, but they become exposed to your entire library, not just CookieMonster.

class PrivateAPI;
class Cookie;

class CookieJar {
public:
    Cookie getCookie(PrivateAPI);
};

class CookieMonster {
public:
    void feed(CookieJar cookieJar);

    bool isHungry();
};

class PrivateAPI {};

void CookieMonster::feed(CookieJar cookieJar) {
    while (isHungry()) {
        cookieJar.getCookie({});
    }
}
Oktalist
  • 14,336
  • 3
  • 43
  • 63
  • I feel like this solves a different problem than the one I asked about. There is no need to restrict the user from calling `getCookie()`. What I want is to hide that public method altogether, to simplify the API.. – Quinchilion Aug 10 '16 at 10:49
  • Regarding idea 1, I don't accept that criticism, `CookieJarPrivate` is hidden. Regarding idea 2, well, `private` members are not hidden, but your users understand the convention that they can ignore `private` members. Idea 2 introduces a new convention that they can ignore members marked with these dummy parameters. Just like `private`, the important thing is what it means to the human reading the code; the syntactic constraint is just a bonus. – Oktalist Aug 10 '16 at 13:14
  • 1
    Your first idea somewhat works, yes. It replaces a public `getCookie()` method with a public `getPrivate()` method, same as what you get with the Pimpl idiom, just without the attached cost. My criticism was towards the other two. Regarding them, yes, I suppose you could look at it that way. In that case, though, I could instead (or in addition) easily introduce a naming convention like `private_getCookie()`, which is arguably easier to notice and won't be as easily suggested by code completion. – Quinchilion Aug 10 '16 at 14:12
0

You should use a private container in your CookieJar class that is being fulled with cookies when the constructor is called. In the code below, I used a vector of the STL C++ library as a container, because it is convenient to use, but you can use something else (array, list, map, etc.), and made the properties of the cookies private. You can hide the monster isHungry attribute too for better encapsulation.

If you want to hide the getCookie() method from the user of the library, then you should make this method private and consider the CookieMonster class as a friend class of the CookieJar, so the CookieMonster will be able to use the getCookie() method and the user will not be able.

    #include<vector>
    using namespace std;
    class Cookie
    {
      private:
       string type;
       string chocolateFlavor;
    }

    class CookieJar {
    friend class CookieMonster;
    public:
        CookieJar(){ 
           //loads a cookie jar with 10 cookies
           for (int i = 0; i = 10; i++) { 
              Cookie cookie; 
              cookieContainer.push_back(cookie);
           }
         }

    private:
        vector<Cookie> cookieContainer;
        Cookie getCookie(){
          //returns a cookie to feed and deletes one in the container
          Cookie toFeed = cookieContainer[0];
          cookieContainer[0] = *cookieContainer.back();
          cookieContainer.pop_back();
          return toFeed;
        }
    }

    class CookieMonster {
    public:
        void feed(CookieJar cookieJar) {
            while (isHungry()) {
                cookieJar.getCookie();
            }
        }
    private:
        bool isHungry();
    }
wayland700
  • 48
  • 8
  • Thanks for the implementation I suppose, but it does not solve my issue with it: The `getCookie()` method is not supposed to be a part of the API and yet it is publicly visible and can be easily confused for a method that is a part of the API. The code was just for illustration. – Quinchilion Aug 09 '16 at 17:11
  • @Quinchilion The getCookie() method is a part of which library? – wayland700 Aug 09 '16 at 17:15
  • The example code _is_ the library. If you wanted to include this file and use the classes in it, you might confuse `getCookie()` for a method that is a part of the interface and might be useful for you, despite it returning a Cookie object you have no use for. – Quinchilion Aug 09 '16 at 17:22
  • @Quinchilion You should then make the method private and give access to CookieMonster with friendship. – wayland700 Aug 09 '16 at 17:29
  • @Quinchilion _"and yet it is publicly visible and can be easily confused for a method that is a part of the API"_ That was the reason I gave you the link to my design approach. – πάντα ῥεῖ Aug 09 '16 at 17:30
0

Another possible approach would be with a kind of double dispatching, like in the following example:

struct Cookie {};

struct CookieJarBase {
    Cookie getCookie() { return Cookie{}; }
};

struct CookieMonster;
struct CookieJar;

struct CookieJar: private CookieJarBase {
    void accept(CookieMonster &);
};

struct CookieMonster {
    void feed(CookieJarBase &);
    bool isHungry();
};

void CookieJar::accept(CookieMonster &m) {
    CookieJarBase &base = *this;
    m.feed(base);
}

void CookieMonster::feed(CookieJarBase &cj) {
    while (isHungry()) {
        cj.getCookie();
    }
}

bool CookieMonster::isHungry() { return false; }

int main() {
    CookieMonster monster;
    CookieJar cj;

    cj.accept(monster);

    // the following line doesn't compile
    // for CookieJarBase is not accesible
    // monster.feed(cj);
}

This way you don't have virtual methods and getCookie is not accessible to the user of the class CookieMonster.
To be honest, the problem moved to feed, that is now unusable from the users and is meant to be used directly be the accept method.

What would solve your problem is a virtual template method, that is simply impossible.
Otherwise, you cannot avoid virtual methods or friend declarations if you don't want to expose unusable methods like in the example above.

Anyway, this at least helps to hide internal methods like getCookie that you don't want to make available.

skypjack
  • 49,335
  • 19
  • 95
  • 187
  • Another interesting solution. Thanks for your time :) Although, I've found a satisfying solution already and will be posting it as an answer shortly. – Quinchilion Aug 09 '16 at 21:45
0

I was also wondering how to properly expose API of my code and I found PIMPL idiom is the best solution. You already mentioned it, but I disagree with sentence:

The Pimpl idiom offers to hide the private members of a class, but does little to disguise the public methods that are not supposed to be a part of the API.

Let's consider we have following code:

namespace Core {

class Cookie {
};

class CookieJar {
 public:
  CookieJar(unsigned _capacity): capacity(_capacity) {}
  bool isEmpty() {
    return count == 0;
  }
  void fill() {
    count = capacity;
  }
  Cookie getCookie() {
    if (!isEmpty()) {
      this->count--;
      return Cookie();
    }
    throw std::exception();
  }
 private:
  const unsigned capacity;
  unsigned count = 0;
};

class CookieMonster {
 public:
  void feedOne(CookieJar* cookieJar) {
    cookieJar->getCookie();
    return;
  }
};

} // namespace Core

Now we want to add API layer, but the requirement is to hide some method and classes of internal implementation. And this can be done without modifying core at all! Just add following code:

namespace API {

class CookieJar {
 friend class CookieMonster;
 public:
  CookieJar(unsigned _capacity) {
    this->impl_ = std::make_unique<Core::CookieJar>(_capacity);
  }
  bool isEmpty() {
    return impl_->isEmpty();
  }
  void fill() {
    return impl_->fill();
  }
 protected:
  std::experimental::propagate_const<std::unique_ptr<Core::CookieJar>> impl_;
};

class CookieMonster {
 public:
  CookieMonster() {
    this->impl_ = std::make_unique<Core::CookieMonster>();
  }
  void feedOne(CookieJar* jar) {
    return impl_->feedOne(jar->impl_);
  }
 protected:
  std::experimental::propagate_const<std::unique_ptr<Core::CookieMonster>> impl_;
};

} // namespace API

Usage example:

int main() {
  {
    using namespace Core;
    CookieJar* jar = new CookieJar(10);
    jar->fill();
    jar->getCookie();
    CookieMonster monster;
    monster.feedOne(jar);
    new Cookie();
  }
  {
    using namespace API;
    CookieJar* jar = new CookieJar(10);
    jar->fill();
    //jar->getCookie(); // <- hidden from API
    CookieMonster monster;
    monster.feedOne(jar);
    //new Cookie(); // <- hidden from API
  }
  return 0;
}

As you can see, with PIMPL we can hide some classes, some public methods. It is also possible to create multiple API layers, without modifying base code. PIMPL works with abstract classes too.

andrzej1_1
  • 1,151
  • 3
  • 21
  • 38