Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to hide an implementation helper template?

Tags:

c++

templates

Suppose that I have two template functions declared in a header file:

template <typename T> void func1(const T& value);
template <typename T> void func2(const T& value);

And suppose that the implementation of these functions (also in a header file and not in a source file, because they are templates) uses some implementation helper function, which is also a template:

template <typename T> void helper(const T& value) {
    // ...
}

template <typename T> void func1(const T& value) {
    // ...
    helper(value);
}

template <typename T> void func2(const T& value) {
    // ...
    helper(value);
}

In any source file that I include the header file, the helper function will be visible. I don't want that, because the helper function is just an implementation detail. Is there a way to hide the helper function?

like image 272
Jesper Avatar asked May 03 '11 12:05

Jesper


7 Answers

A common approach (as used in many Boost libraries, for example) is to put the helper in a namespace called details, possibly in a separate header (included from the "public" header).

There's no way to prevent it from being visible, and callable, but this quite clearly indicates that it is part of the implementation, not the interface.

like image 143
Mike Seymour Avatar answered Oct 06 '22 21:10

Mike Seymour


The established precedent is to put that sort of thing in a specially (i.e. consistently) named nested namespace. Boost uses namespace details, Loki uses namespace Private. Obviously nothing can prevent anyone from using the contents of those namespaces, but both names convey the meaning that their contents aren't intended for general consumption.

That being said, an easy alternative is to turn func1 and func2 from free function templates into static member function templates of some common class; this way, helper can simply be a private member of said class, invisible to the outside world:

struct funcs {
    template<typename T>
    static void func1(T const& value) {
        // ...
        helper(value);
    }

    template<typename T>
    static void func2(T const& value) {
        // ...
        helper(value);
    }

private:
    template<typename T>
    static void helper(T const& value) {
        // ...
    }
};
like image 30
ildjarn Avatar answered Oct 06 '22 21:10

ildjarn


Two options off the top of my head:

  1. Move all the implementation to an hpp file which you include at the bottom of your h file.
  2. Refactor your code as class templates, then make the helpers private.
like image 43
John Dibling Avatar answered Oct 06 '22 23:10

John Dibling


Since the user of your code needs to see the full definition of the func1 function, its implementation, nor its helper function implementation, cannot be hidden.

But if you move the implementation into another file, the user will only have to be confronted with the template declaration:

//templates.h
template< typename T > void f1( T& );

#include <templates_impl.h> // post-inclusion

And the definition:

// templates_impl.h
template< typename T > void f1_helper( T& ) {
}

template< typename T > void f1( T& ) {
   // the function body
}
like image 31
xtofl Avatar answered Oct 06 '22 21:10

xtofl


In C++20 you can now use modules. For this you could create a module some_module.cppm holding all functions and marking only the interface (either the individual functions or a namespace) with export while not exposing the helper functions:

// some_module.cppm
export module some_module;

template <typename T> 
void helper(const T& value) {
  // ...
}

export
template <typename T>
void func1(const T& value) {
  // ...
  helper(value);
}

export
template <typename T>
void func2(const T& value) {
  // ...
  helper(value);
}

In the case above only the functions func1 and func2 are exported and can be accessed inside main.cpp:

// main.cpp
#include <cstdlib>
import some_module;

int main() {
  func1(1.0);
  func2(2.0);
  return EXIT_SUCCESS;
}

In clang++12 you can compile this code with the following three commands:

clang++ -std=c++2b -fmodules-ts --precompile some_module.cppm -o some_module.pcm
clang++ -std=c++2b -fmodules-ts -c some_module.pcm -o some_module.o
clang++ -std=c++2b -fmodules-ts -fprebuilt-module-path=. some_module.o main.cpp -o main
./main
like image 21
2b-t Avatar answered Oct 06 '22 22:10

2b-t


I would (as said before) make a template class, make all functions static and the helper function private. But besides that I'd also recommend making the constructor private as shown below:

template <typename T>
class Foo{
public:
  static void func1(const T& value);
  static void func2(const T& value);
private:
  Foo();
  static void helper(const T& value);
}

When you make the constructor private, the compiler won't allow instances of this template class. So the code below would become illegal:

#include "foo.h"

int main(){
  int number = 0;
  Foo<int>::func1(number); //allowed
  Foo<int>::func2(number); //allowed
  Foo<int>::helper(number); //not allowed, because it's private
  Foo<int> foo_instance; //not allowed, because it's private
}

So why would someone want this? Because having different instances that are EXACTLY the same is something you probably never want. When the compiler tells you that the constructor of some class is private, then you can assume that having different instances of it would be unnecesarry.

like image 45
user3635700 Avatar answered Oct 06 '22 21:10

user3635700


I know you mean that you want to hide it so finely that callers have no way to find your helper functions as long as they don't change your code file. I know it so well because I have very similar needs recently.

So how about wrapping your helper functions in an anonymous namespace? This is recently the most elegant style I found:

namespace YourModule
{
namespace
{//Your helper functions}
//Your public functions
}

This practice effectively hides your internal functions from the outside. I can't find any way that a caller can access functions in anonymous namespaces.

It's usually not a good practice, as answered by @user3635700 , to convert your namespace to a class full of static functions, especially when you have templates of static variables like:

template <uint8_t TimerCode>
static uint16_t MillisecondsElapsed;

If this variable appears in a class, you'll have to initialize it somewhere outside the class! However, you can't do it because it's a template! WTF!

It seems that an anonymous namespace in the header file, which is criticized by some, is the only and the most perfect solution to our needs.

like image 44
埃博拉酱 Avatar answered Oct 06 '22 21:10

埃博拉酱