6

I want to start throwing some interfaces into my C++ code to make it easier for me to unit test using mocks.

The problem with this is returning abstract classes from a method in C++ is a pain. You can't return by value so you need to return a pointer or a reference.

Given all of the developments in C++ in the last six or seven years, I thought I'd ask if maybe we had a better way to return an abstract base class. An interface without the noise would look something like this, but I'm sure this isn't possible.

IBaseInterface getThing() {return DerivedThing{};}

The way that I remember doing this in the past is to use a pointer (probably a smart pointer now):

std::unique_ptr<IBaseInterface> getThing() {return std::make_unique<DerivedThing>();}

The problem with the pointer is that I'm never actually planning to take advantage of nullptr so the overhead and noise of dealing with a pointer rather than a value gains me no value as a reader.

Is there a better way that I don't know to handle this?

user1217210
  • 113
  • 5
  • 1
    It's not clear to me why unit-testing with mocks requires you to have abstract interfaces. – SergeyA Mar 17 '16 at 16:41
  • 1
    Note that your 1st example won't work, you need to return a pointer or a reference. – πάντα ῥεῖ Mar 17 '16 at 16:43
  • It is exceedingly difficult to build polymorphic types that can never be in an empty state without planning to `exit` in some extreme error conditions. If you know something isn't nullptr, you can just not check for it: the real cost of `unique_ptr` is it is heap-allocated (on the free store) not in automatic storage. However, if you are willing to limit the size of the objects and admit the rare `exit` in extreme cases, a smart union type that publishes an interface (either via a vtable, or via type erasure) can work, with various amounts of work. – Yakk - Adam Nevraumont Mar 17 '16 at 16:44
  • @SergeyA - The abstract interfaces let me replace dependencies with mocked versions and pass these into a constructor. That way am only testing the class under test and not the dependencies involved. If you know a better way, I'm all ears. – user1217210 Mar 17 '16 at 16:46
  • @πάνταῥεῖ The first one was supposed to be the ideal result. I'll update the code to say this. Good point. – user1217210 Mar 17 '16 at 16:47
  • You could have a type that wraps the implementation. Have a look at the pimpl idiom. This can be leveraged to allow for a "polymorphic" type that hides the polymorphism inside its own implementation. – juanchopanza Mar 17 '16 at 16:52
  • @user1217210, the way I am usually doing unit-testing is by mocking any dependecies at the call site. So I need no interfaces at all. – SergeyA Mar 17 '16 at 16:52
  • Using typedefs like `typedef std::unique_ptr IBaseInterface` might make your interface more readable. Another possibility might be to write your own wrapper struct that only allows non-null pointers (maybe as r-value-reference) and provide an operator to `IBaseInterface&`.... – Simon Kraemer Mar 17 '16 at 16:52
  • 3
    You should not compromise your software architecure for unit tests. – Christian Hackl Mar 17 '16 at 16:55
  • 1
    @ChristianHackl That's an arguable statement. It often improves the architecture doing so. – πάντα ῥεῖ Mar 17 '16 at 17:17
  • If you want dependency injection, why not just go straight for it and use [a dependency injection library](https://boost-experimental.github.io/di/)? – ildjarn Mar 18 '16 at 07:05
  • @πάνταῥεῖ: I'd say a good architecture is always testable, anyway. Good architecture implies testability. But the reverse is not true; testability does not imply good architecture. For example, we have the C++ guideline that dynamic allocation should be minimised. Adding extra dynamic allocation just for testing is not worth the trouble IMO, even if it adds testability. Personally, I'm a big fan of an approach in which one tries to turn as much code as possible into libraries, with extremely thin application layers, the libraries also being the smallest units which are individually tested. – Christian Hackl Mar 18 '16 at 15:05

1 Answers1

4

EDIT: provides complete example, including making the polymorphic handle copyable.

#include <iostream>
#include <utility>
#include <memory>

struct IBaseInterface {
    IBaseInterface() = default;
    IBaseInterface(IBaseInterface const&) = default;
    IBaseInterface(IBaseInterface &&) = default;
    IBaseInterface& operator=(IBaseInterface const&) = default;
    IBaseInterface& operator=(IBaseInterface &&) = default;
    virtual ~IBaseInterface() = default;

    virtual std::unique_ptr<IBaseInterface> clone() const = 0;
    virtual void do_thing() = 0;
};

struct handle
{
    handle(std::unique_ptr<IBaseInterface> ptr)
    : _impl(std::move(ptr))
    {}

    handle(handle const& r)
    : _impl(r._impl->clone())
    {}

    handle(handle && r)
    : _impl(std::move(r._impl))
    {}

    handle& operator=(handle const& r)
    {
        auto tmp = r;
        std::swap(_impl, tmp._impl);
        return *this;
    }

    handle& operator=(handle && r)
    {
        _impl = std::move(r._impl);
        return *this;
    }


    // interface here
    void do_thing() { _impl->do_thing(); }

private:
    std::unique_ptr<IBaseInterface> _impl;
};

struct DerivedThing : IBaseInterface
{
    std::unique_ptr<IBaseInterface> clone() const override
    {
        return std::make_unique<DerivedThing>(*this);
    }

    void do_thing() override
    {
        std::cout << "I'm doing something" << std::endl;
    }

};

handle make_thing()
{
    return handle(std::make_unique<DerivedThing>());
};

int main()
{
    auto a = make_thing();
    auto b = a;

    a.do_thing();
    b.do_thing();

    return 0;
}

Now use your handle as if it had (moveable) value semantics

Richard Hodges
  • 68,278
  • 7
  • 90
  • 142
  • Yep, that's the one. It can be extended it to use a `clone()` method to implement copy and assignment (or implement a `value_ptr` and use that.) – juanchopanza Mar 17 '16 at 16:56
  • of course just call clone() from the copy operator. don't forget that clone() should return a unique_ptr. – Richard Hodges Mar 17 '16 at 17:07
  • Do I understand this solution correctly?: For each abstract base class `B`, one would have to define a concrete (i.e. returnable by value) class `BHandle` which (a) wraps a `unique_ptr`, (b) has the same copy/assignment semantics as a `unique_ptr` but (c) exposes the same interface as `B` and thereby avoids the need for callers to dereference it (i.e. no `*bHandle` nor `bHandle->...`). The interface is implemented by forwarding to the object referenced by the contained smart pointer. (d) Copy construction is implemented by having a `clone` method in `B` ("virtual copy constructor" pattern). – stakx - no longer contributing Aug 03 '16 at 20:51
  • 1
    @stakx pretty much, yes. Templates can help with the boilerplate workload. – Richard Hodges Aug 03 '16 at 21:33