Function declaration best-practice?

Let's say I'm developing a logging functionality. Inside `logging.h` I declare the function to be used by the application later on.

1
2
3
4
5
6
7
8
9
// logging.h

#include <string>

namespace logging {

void LogThis(const std::string& text);

};  // namespace logging 



Its definition is obviously inside `logging.cpp`:


1
2
3
4
5
// logging.cpp

void logging::LogThis(const std::string& text) {
  std::cout << "Log: " << text << '\n';
}



Now lets pretent that my `LogThis` function's work is split up into some smaller helper functions. They're not part of the logging interface. Let's take a `Prettify` function as an example.


1
2
3
4
5
6
// logging.cpp

void logging::LogThis(const std::string& text) {
  Prettify(text);
  std::cout << "Log: " << text << '\n';
}



**My question is: Where do I put the function declaration of `Prettify`?** I shouldn't include it in the `logging.h` header file, because then it can be called by other compilation units and its not part of the interface. So just put it inside `logging.cpp` instead like this?


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// logging.cpp

namespace logging {

void Prettify(std::string& ugly_text);

void LogThis(const std::string& text) {
  Prettify(text);
  std::cout << "Log: " << text << '\n';
}

void Prettify(std::string& ugly_text) {
  // making it pretty...
}

}



I'm looking for some best practices / rules of thumb / opinions on this :) Thanks in advance!
Last edited on
I would put Prettify in logging.cpp, but not in the logging namespace.
And I would make it static so it is impossible to link to from other translation units.
Putting it in logging.cpp like that is fine. Best practise would be to put it into an anonymous namespace, to make sure it has no external visibility.
I have somewhat different suggestion:

1. Users of your logging interface may want to write their own logging function, but retain
some internals.
2. You may want to have different behavior (ex. different logging color in console) depending on what's being logged.
3. You want to conditionally enable disable logging.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// logging.h

#include <string>

namespace logging
{
      namespace detail
      {
            void Prettify(std::string& ugly_text);
      }

      void LogThis(const std::string& text);

};  // namespace logging

// conditionally use logging functionality
#ifdef USE_LOGGING
#define TRACE(text) logging::LogThis(text)
#else
#define TRACE(text) static_cast<void>(0)
#endif 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// logging.cpp

namespace logging
{
      namespace detail
      {
            // TODO: provide overloads or color enum for different formatting behavior
            void Prettify(std::string& ugly_text) {
                    // making it pretty...
            }
      }

      void LogThis(const std::string& text) {
             detail::Prettify(text);
             std::cout << "Log: " << text << '\n';
      }
}


Now for point one, some user or you can write it's own logging function outside this header, but reuse your formatting internals.

In point 2, you can achieve different behavior by using inline namespaces or by providing color enum and pass it to prettify function. Or you could write formatting overloads all in detail namespace.

And finally, logging will happen only if USE_LOGGING is defined, otherwise logging is disabled.

to use logging you would write:

1
2
#define USE_LOGGING
TRACE("logging test");


if USE_LOGGING is not defined TRACE macro expands to empty statement, and nothing happens.

If you want particular logging event to always happen simply call function instead of macro.

However it would be better to make a logging class probably, to be able to better reuse code.
Last edited on
Topic archived. No new replies allowed.