Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Proper organization for a mixture of templated and non-templated functions not accessible to the end-user

In a project I am working on, I am currently implementing an algorithm which is templated. I had some problems organizing my function declarations and definitions, because templates are involved (I asked a question here about how to deal with non-templated functions "grouped" together with the templated ones). This, however, made me wonder about a general proper organization of such files.

I wanted to separate all the definitions from all the declarations (just for readability purposes, even if I had to include templated definitions back into declarations).

The answers I got suggested organization such as this:

  • The declarations file algo.h for all the function declarations
  • The templated definitions file algo.tpp which will be included in algo.h (templates need declarations at compile time)
  • The (non-templated) definitions file algo.cpp not included anywhere.

This works well if all the defined functions are supposed to be visible to the end-user (i.e. all the declarations are in the algo.h file). However, sometimes I like to break my big functions into smaller functionalities, but I would like the end-user to be able to access only the "big function" and not their sub-parts. In a non-templated set-up, I would do this in this way:

  • The declarations file algo.h would contain only the declarations meant for the end-user (which will become accessible through the include)
  • The definitions of those functions would go to algo.cpp
  • The definitions (and declarations) of the sub-functionalities would only be present in the algo.cpp, allowing my big function in algo.cpp (the one declared in algo.h) to use them, but not making them accessible to the end-user.

This does not work any more if those sub-functionalities are not themselves templated, but are used by the templated function. The templated sub-functionalities can go into the .tpp file, get used by the templated "big function", and everything is good.

However, if those functionalities are non-templated, they would cause a multiple definitions error (which is what my previous question was about) if placed in the .tpp file. On the other hand, if they are in the separate .cpp file, they either become accessible to the end-user (if I put the declarations in the .h file), or become inaccessible to the big function meant to use them (if I do not put the declarations outside the .cpp file).

What is the proper way of organizing functions, some templated and some not, some of which are supposed to be accessible to the end-user, and some not, into multiple files?

Ideally, (for completeness), the answer I'm looking for would address the placement of the following (in the .h, .tpp, .cpp, or other appropriate files):

  • templated client functions
  • non-templated client functions
  • templated client functions
  • non-templated client functions
  • declarations of all of the above (when declarations are used)

Short almost-pseudo-code example of the functionality I want to have (without the file-separation)

    // Templated sub-functionality, used by bigFunctionality,
    // but ideally not accessible to the end-user
    // This might not be possible since it is templated,
    // so I am content with putting it in the .tpp file
    template <typename Compare>
    void subFunctionality(Compare order, .. args ..){ /* impl */ }

    // Non-templated sub-functionality, used by
    // bigFunctionality, but NOT accessible to the end-user
    void moreSubFunctionality(.. args ..) { /* impl */ }


    // the main functionality, meant to be
    // accessible to everybody across all files:
    template <typename Compare>
    void bigFunctionality( .. non-templated args ..., Compare order){
         subFunctionality(order, .. args ..);
         moreSubFun(.. args ..);
         // more stuff
    }

Once again, I am looking how to separate this in to multiple files (even if they are included in each other, as it has to be done with templates), partially for readability purposes and partially for accessibility.

Just for clarifications, these are algorithms --> functions, and not classes. (I know that putting templated and non-templated functions in to the same templated class would solve my problems).

PS: I know the title of the question is very big and long, so if somebody has an idea about shortening it, I would be more than happy for the suggestions/edits

like image 893
penelope Avatar asked Oct 20 '22 03:10

penelope


1 Answers

This is an unfortunate side-effect of the absence of modules in C++: what is declared is necessarily visible.

In general, the guideline is to simply use a "private" namespace (nested within the user-visible namespace(s)):

  • use a clear name (such as internal)
  • and declare those helper functions only in that namespace in your .tpp file

Then, you document that this namespace is internal and non-suitable for client callers. If possible, you also annotate the namespace/functions so that no documentation is generated for them.

At this point, those functions are clearly private and hidden.

If you wish to go one step further, you may simply declare them private in a class (*), and then have only your functions be friend of this class. Then, any attempt to use them will result in the compiler emitting an error: they are visible, but not accessible. However, most people (starting with Boost developers) just do not bother, and I certainly do not either.

(*) The class essentially replaces the private namespace.

like image 141
Matthieu M. Avatar answered Oct 22 '22 17:10

Matthieu M.