3

In the 2017 cppcon videos, I came across a talk by Klaus Iglberger which was entitled "Free Your Functions!". In this talk, the speaker talked about how switching to free functions could easy up the process of testing private methods (See at 19:00). The idea is that you pull the private method out of the class (you make it a free function) and it becomes testable.

At first, I found the idea interesting, but then the more I thought about it, the less I understood how this is actually supposed to work. For example, let's say I have the following (dummy) class:

class SomeClass
{
public:
    SomeClass();
    ~SomeClass();

    void someTask();

private:

    void someComplexTask();
    void someOtherComplexTask();

};

void SomeClass::someTask()
{
    someComplexTask();
    someOtherComplexTask();
}

// private tasks implementations...

Then someComplexTask() and someOtherComplexTask() are private methods. This means that they are implementation details, i.e. that they can only be called inside SomeClass (or friends). It seems to me that if you make them free functions, yes they become testable, but they are no longer private, nor a mere implementation detail specific to SomeClass. In fact, they can be called from anywhere in the code...

So my question is: why is Mr. Iglberger's point valid?

BobMorane
  • 2,070
  • 1
  • 13
  • 29
  • 1
    If anyone decided to watch the video here is the [link](https://youtu.be/WLDT1lDOsb4?t=19m1s) with the exact time – smac89 Mar 28 '18 at 01:27
  • @smac89 Thanks, I updated my question with your link. – BobMorane Mar 28 '18 at 01:29
  • I think it entirely depends on the use of those private methods. In that video, the example he used could very easily be turned into a free function because it was very generic in purpose and did not depend on anything within the class. I wouldn't take that video literally. I think the point is that you should be able to find places in your code where a free function will work as well as a member function because free functions are more easily tested. Having a private `reset` method which could be turned into a testable `reset` (free) function is just bad design – smac89 Mar 28 '18 at 01:42
  • I think what he suggests is to refactor the private function you wish to test as a public function in a utility class. And this still sounds better to me then the concept of the free function in the header of a class, but that is just my humble opinion. – Rann Lifshitz Mar 28 '18 at 01:49

2 Answers2

7

This is a clear indication that you have a design flaw. If you have a private function that you need to test and you have to bend backwards to make it work then something is wrong. You have missed something. Your design doesn't work.

His point is not to just make private functions free. He is not saying: "get all your private functions and make them free functions". He is saying that functionality that needs to be tested shouldn't be an implementation detail because if you need to test it that is an indication the functionality is useful.

Please pay close attention to the transformation he does to the code:

initial code:

class X
{
public:
 void doSomething( ... ) {
    ...
    resetValues();
    ...
 }
 ...
private:
 void resetValues() {
     for( int& value : values_ )
        value = 0;
 }
 std::vector<int> values_;
};

He pulls resetValues out of X but it makes it operate on a std::vector<T>, not on X:

void resetValues( std::vector<int>& vec )
{
 for( int& value : vec )
   value = 0;
}

Now resetValues is a functionality that can be reused and tested. As it truly has nothing to do with X, but with resetting all values of a vector it is a valid design to make it a free function instead of a private X method.

I like how Rann Lifshitz put it in his comment:

I think the better way to go here is to understand that some private functions are, in fact, common utility functions

bolov
  • 58,757
  • 13
  • 108
  • 182
  • 1
    I think the better way to go here is to understand that some private functions are, in fact, common utility functions that should be public in a general purpose utility class. This approach sound a lot better to me then using a free function, which is IMHO a some what strange bird in the land of OOP. – Rann Lifshitz Mar 28 '18 at 01:52
  • 3
    @RannLifshitz: *"common utility functions that should be public in a general purpose utility class"* - a utility namespace is a better fit than a class, for functions that don't share data or duties: they decouple logical grouping from header files, and - for better more often than worse - give the client programmer the freedom to decide whether to use namespaces or specific functions or keep them explicit in client code. – Tony Delroy Mar 28 '18 at 02:09
0

I have watched the video too. But, I have a few disagreements.

1- Does your method require accessing fields? If not, it does not belong to the class. But if it does, they need the fields. The free functions do not have access to the fields unless you pass them as function arguments. Please consider that free functions should not be looked as public function.

2- Not everything is supposed to be free function. But it is a good practice to avoid putting everything in the class when they are not necessary.

3- Private function are not often supposed to be tested. But if you insist, you might be able to perform such as invalid hack (which not always works as mentioned in the comments):

#define class struct
#define private public
#define protected public

#include "library.h"

#undef class
#undef private
#undef protected

Freeing your function is neater but not more feasible.

Arash
  • 1,984
  • 9
  • 15
  • Would be great if you explain the reason you don't like this answer a few seconds after posted. – Arash Mar 28 '18 at 01:46
  • 1
    Oh God No! What hath thou wrought? Just use a proper testing framework. Don't do this please – smac89 Mar 28 '18 at 01:47
  • 1
    the defines you stated here are exactly what Klaus Iglberger mentioned as design breaking solutions. – Rann Lifshitz Mar 28 '18 at 01:48
  • 2
    It is **illegal** to define C++ keywords. – bolov Mar 28 '18 at 01:49
  • @RannLifshitz, by using the term `ugly` I described them well as a method which should not be used in a good practice. But unit test is not the main source. – Arash Mar 28 '18 at 01:50
  • @Arash : this is not a matter of good or bad practices - using these definitions breaks the format of the language and IMHO is a 'crime against humanity' - ie. will not pass a code review. – Rann Lifshitz Mar 28 '18 at 01:54
  • 1
    @Arash no. `ugly` describes a valid code (from the point of the standard) that uses some anti-pattern or is plain unreadable, un-maintainable etc. What you have is **illegal** C++ code. – bolov Mar 28 '18 at 01:55
  • `#define class struct` also breaks every usage of `template ...` in the library. There are a class of similar problems with declarations potentially conflicting when `library.h` includes other headers, then not matching library objects. Similarly, if you were going to try that hack, you'd be better off including library.cpp directly so as least the declarations and definitions for things the compiler sees in library match - less chance of untoward behaviour but anything's possible. – Tony Delroy Mar 28 '18 at 01:59
  • @TonyDelroy, thanks for mentioning the template counter example. – Arash Mar 28 '18 at 02:48