2

A class concerned with a specific task relies on a certain 3rd party library to perform this task (lets say JSON serialization). The use of the library should be transparent for the client and none of the 3rd party code classes or data types are referenced in the public interface of the class.

Before I introduced a private method, I could just add the required #includes to the .cpp file. However, now that I declared a private method depending on the 3rd party code I have to pull up some #includes to the header file which in turn leads to inclusion of the respective headers in all other files including the header file of my class.

I was thinking about using functions instead of private methods so I would not have to declare the functions in the header. Of course I would have to pass references to the fields I'm using to these functions. Is this a reasonable way to work around this issue or are there best practices to achieve this kind of encapsulation while using private methods?

lex82
  • 10,346
  • 2
  • 37
  • 63
  • 4
    You could use forward declaration. – Robin Krahl Jul 30 '15 at 12:40
  • 9
    You might want to consider the [PIMPL idom](http://stackoverflow.com/questions/60570/why-should-the-pimpl-idiom-be-used). – Chris Drew Jul 30 '15 at 12:40
  • 3
    Interesting reading: [how-non-member-functions-improve-encapsulation](http://www.drdobbs.com/cpp/how-non-member-functions-improve-encapsu/184401197?pgno=1) – Jarod42 Jul 30 '15 at 12:44
  • Thanks @chris-drew, PIMPL seems to be what I'm looking for. – lex82 Jul 30 '15 at 12:47
  • 1
    I'm not sure how forward declaration solves my problem. Which method/function should be declared where? – lex82 Jul 30 '15 at 12:48
  • 1
    @lex82: Unless you need **definition** of some 3rd party elements in your header (as inner class, enum defined inside class), you may forward each type of your 3rd party header without including its header in .h but only in cpp. – Jarod42 Jul 30 '15 at 13:39
  • 1
    The Pimpl idiom is far more heavy-weight than what you need here. Using a non-member function is much simpler and straight-forward. – Rob K Jul 30 '15 at 14:06

3 Answers3

4

let say you have to add those private methods

  • Json::Node AsJson() const;
  • std::string Serialize(const Json::Node& root) const;
  • Json::Node Unserialize(const std::string& document) const;
  • void InitFrom(const Json::Node&);

FORWARD DECLARATION

Assuming Json is a namespace, in header you only need forward declaration

namespace Json
{
    class Node;
}

instead of

#include <3rdLibrary/Json.hpp>

if Json is a class and so Json::Node an inner class, you can't use forward declaration

FREE FUNCTIONS If the function doesn't require private access (and some other things as no virtual), you may create free functions (in unnamed namespace or static)

so nothing in header, and in cpp:

#include <3rdLibrary/JsonCpp.hpp>
namespace {
    Json::Node AsJson(const MyClass& myclass) {/**/}
    std::string Serialize(const MyClass& myclass, const Json::Node& root) {/**/}
    Json::Node Unserialize(const MyClass& myclass, const std::string& document) {/**/}
    void InitFrom(MyClass& myclass, const Json::Node&){/**/}
}
MyClass::foo()
{
    Serialize(*this, AsJson(*this));
    /**/
}

PIMPL IDIOM Pimpl idiom is an other alternative

// header.hpp

class MyClass
{
public:
    ~MyClass();
    MyClass(const MyClass&); // = delete ?
    MyClass& operator =(const MyClass&); // = delete ?
    MyClass(MyClass&&); // = delete ?
    MyClass& operator =(MyClass&&); // = delete ?

    // previous public methods.
private:
    struct Pimpl;
    std::unique_ptr<Pimpl> pimpl;
};

And in source

// MyClass.cpp

struct Pimpl
{
    // What you should have done in MyClass
};

MyClass::~MyClass() = default; // which destroys Pimpl and should know Pimpl definition
                               // that's why it is not in header

ReturnType MyClass::publicMethod(Args args) {return pimpl->publicMethod(args);} // Same for all public methods.

Note: it may be possible to hide with pimpl idiom only some part of implementation

INTERFACE

And finally, similarly to pimpl idiom, you may use interface

// IMyClass.hpp
class IMyClass
{
public:
    virtual ~IMyClass() = default;
    IMyClass(const IMyClass&); // = delete ?
    IMyClass& operator =(const IMyClass&); // = delete ?
    IMyClass(IMyClass&&); // = delete ?
    IMyClass& operator =(IMyClass&&); // = delete ?

    // previous public methods but virtual.
};

std::unique_ptr<IMyClass> makeMyClass(Args);

and implement MyClass normally (with override) (its header is only used by its cpp file)

and implement also

std::unique_ptr<IMyClass> makeMyClass(Args args) { return std::make_unique<MyClass>(args); }

Note: it may be possible to expose via interface only some part (to hide only some part of code).

Jarod42
  • 173,454
  • 13
  • 146
  • 250
  • Nice thorough answer. One point though, with forward declaration you cannot return Json::Node object from the functions. It has to be reference or pointer. unique_ptr will do it nicely. – Paani Jul 30 '15 at 14:34
  • 1
    @Paani: We don't require definition for return type or argmument in declaration [Demo](http://coliru.stacked-crooked.com/a/7b59b82b9e3031a3). but if a member `Json::Node` is added in `MyClass`, then forward declaration is not sufficient (and require the usage of reference or pointer). – Jarod42 Jul 30 '15 at 14:44
  • Yes indeed. They are just declaration! – Paani Jul 30 '15 at 14:56
  • Great summary of the different options. Thanks a lot! – lex82 Jul 31 '15 at 11:56
2

Use a free (non-member) function in your implementation file, not a private method. Many consider private methods to be a code smell anyway.

This article by Scott Meyers (which @Jarod42 mentioned in a comment) is an excellent discussion of how and why to do it.

ETA: It's been awhile since I read Scott Meyers' article, and I forgot his focus was more on functions that might be part of the public interface to a class from the class creator's perspective. But think about his Wombat example from a class user's perspective. What you want to do is add to the interface of the class you're using, with out exposing that implementation detail to your class's users.

What I'm talking about are "hidden helpers", functions that don't need direct access to the internals of the class, but are used by the public functions of the class to do work, which is what it sounds like you've described. These are functions that are only visible inside the implementation file of a class, and to which a member function would pass copies or references to its data members.

A trivial example might be something like this:

namespace {

int frob( int coordinate )
{
    return coordinate * 3 + 9;
}

} // end anonymous namespace

void foo::shift_out()
{
    for ( auto & coord : m_coords )
    {
        coord = frob( coord );
    }
} 

So in this example, foo::shift_out() uses frob() to do the necessary work while frob() knows nothing about the internals of class foo and only the class foo implementation knows about the existence of frob().

As to private methods being a code smell, searching here on StackOverflow will give you a number of good discussions of the issue. It's seen as an indicator that the class may be trying to take on too much responsibility, which it should delegate to other classes which are then used to compose the original class.

Rob K
  • 8,326
  • 2
  • 29
  • 33
  • I've been doing this more and more. Of course you still need it to be a `friend` if `private` data access is required, and if the function's signature references the 3rd party library (in a way that is not satisfied by forward declarations), then you're back where you started. – Lightness Races in Orbit Jul 30 '15 at 14:07
  • Can you elaborate a bit on why private member functions are considered a code smell? Scott says to prefer non-member non-friend functions if they only use the _public_ parts of the class. Functions that use the private parts of the class are legitimate member functions some of which may be public, some private. – Chris Drew Jul 30 '15 at 16:15
  • It had been a while since I read that article. I'd forgotten his focus was on non-member non-friends as part of the interface to the class, not as hidden helpers. I will edit my answer to elaborate... – Rob K Jul 30 '15 at 18:27
  • Thanks, I think I'll start with this solution. However, I chose Jarods answer because he also covered the other patterns. – lex82 Jul 31 '15 at 11:58
  • He deserved it. His answer was much more comprehensive than mine. – Rob K Jul 31 '15 at 15:08
0

PIMPL idiom will suit you best. In the main interface class just expose the public members and have a pointer to implementation.

class MyImpl;

class InterfaceClass
{
public:
    // Public methods go here.

private:
    MyImpl *pImpl;
};

Define the MyImpl class separately and it's header does not need to part of your interface files.

Paani
  • 538
  • 2
  • 10
  • Pimpl is not what is called for here. The entire implementation doesn't need to be totally hidden, just the one function. – Rob K Jul 30 '15 at 13:59
  • I've never liked PIMPL. I respect it but I don't like it. Far too heavy. – Lightness Races in Orbit Jul 30 '15 at 14:09
  • @Rob K: Only one method may need hiding today but that list will likely expand in future. Once you have PIMPL in place, you can easily extend in future. – Paani Jul 30 '15 at 14:13
  • So you've written a bunch of extra code, all of which has to be tested and verified correct, for something you *might* need in the future? Free functions can easily be pulled into a pimpl idiom if it later becomes necessary. The more code you write, the more likely you are to get something wrong. "Do the simplest thing that could work" not the more complicated thing because "you're not going to need it" (YAGNI). Time spent writing code you don't need is time not spent writing code you do need, and worse, time not spent testing. – Rob K Jul 30 '15 at 18:12