Debug function that "disappears" in some cases - c++

Suppose I have a debug function that is defined like this:
namespace debug {
void report(std::string message);
}
Can I pull some compiler trick that will, when compiled, replace every call safely with a nop. I dont want to call an empty function, I want to not call the function at all.
If it is possible... can I make a namespace "disappear", too?
Debug executables will be compiled with the symbol DEBUGEXECUTABLE defined (I can imagine some tricks with macros).

You can do something like this:
namespace debug
{
void report(std::string message); // ToDo - define this somewhere
}
namespace release
{
template <class Y>
void report(Y&&){} // Intentionally do nothing
}
#if defined(DEBUGEXECUTABLE)
namespace foo = debug; // set foo to the debug namespace
#else
namespace foo = release; // set foo to the release namespace
#endif
Then use foo::report in your code. I like this since it minimises the use of preprocessor macros and keeps any compiler errors broadly similar across the debug and release configurations.
Passing a r-value reference in the release mode will allow the compiler to optimise out any anonymous temporaries. For the debug family of functions, you ought to pass strings by constant reference though to avoid any possiblity of a value copy being taken: void report(const std::string& message);

This is as optimal as I can make it.
We define DEBUG to have a report that does something, and leave it out it to do nothing: (or we can use whatever symbol you are using in your build process to distinguish debug and opt from production code)
#define DEBUG
We create two namespaces. One is called debug, the other release. In each we create an anonymous namespace, which makes it easy for the compiler to detect and discard unused functions:
namespace debug {
namespace {
void report(std::string const& s) {
std::cerr << s << "\n"; // sample implementation
}
}
}
namespace release {
namespace {
template<class T>
void report(T&&) {} // Or `class...Ts` and `Ts&&...` to handle more than 1 argument optionally.
}
}
Here we create a namespace alias that differs in release and debug:
#ifdef DEBUG
namespace report=debug;
#else
namespace report=release;
#endif
And our main:
int main() {
report::report("hello");
}
We can see the results of this under gcc 4.9 with DEBUG defined and not over at godbot. As you can hopefully see, when #define DEBUG is not defined, the compiler produces nothing but an empty main.
If it is defined, it compiles to what you'd expect.

namespace debug {
#ifdef DEBUGEXECUTABLE
void report(std::string message);
#else
inline void report(std::string message)
{
//nop - compiler should optimize it
}
#endif
}

Related

Debug Assertion Failed Expression __acrt_first_block == header

I have been trying to figure out why this is happening and maybe it is just due to inexperience at this point but could really use some help.
When I run my code, which is compiled into a DLL using C++20, I get that a debug assertion has failed with the expression being __acrt_first_block == header.
I narrowed down where the code is failing, but the weird part is that it runs just fine when I change the Init(std::string filePath function signature to not contain the parameter. The code is below and hope someone can help.
Logger.h
#pragma once
#include "../Core.h"
#include <memory>
#include <string>
#include "spdlog/spdlog.h"
namespace Ruby
{
class RUBY_API Logger
{
public:
static void Init(std::string filePath);
inline static std::shared_ptr<spdlog::logger>& GetCoreLogger() { return coreLogger; }
inline static std::shared_ptr<spdlog::logger>& GetClientLogger() { return clientLogger; }
private:
static std::shared_ptr<spdlog::logger> coreLogger;
static std::shared_ptr<spdlog::logger> clientLogger;
};
}
Logger.cpp
namespace Ruby
{
std::shared_ptr<spdlog::logger> Logger::coreLogger;
std::shared_ptr<spdlog::logger> Logger::clientLogger;
void Logger::Init(std::string filePath)
{
std::string pattern{ "%^[%r][%n][%l]: %v%$" };
auto fileSink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(filePath, true);
// Setup the console and file sinks
std::vector<spdlog::sink_ptr> coreSinks;
coreSinks.push_back(std::make_shared<spdlog::sinks::stdout_color_sink_mt>());
coreSinks.push_back(fileSink);
// Bind the sinks to the core logger.
coreLogger = std::make_shared<spdlog::logger>("RUBY", begin(coreSinks), end(coreSinks));
// Set the Patterns for the sinks
coreLogger->sinks()[0]->set_pattern(pattern);
coreLogger->sinks()[1]->set_pattern(pattern);
// Tell spdlog to flush the file loggers on trace or worse message (can be changed if necessary).
coreLogger->flush_on(spdlog::level::trace);
// Set the default level of the logger
coreLogger->set_level(spdlog::level::trace);
// Do the same for the client logger
std::vector<spdlog::sink_ptr> clientSinks;
clientSinks.push_back(std::make_shared<spdlog::sinks::stdout_color_sink_mt>());
clientSinks.push_back(fileSink);
clientLogger = std::make_shared<spdlog::logger>("APP", begin(clientSinks), end(clientSinks));
clientLogger->sinks()[0]->set_pattern(pattern);
clientLogger->sinks()[1]->set_pattern(pattern);
clientLogger->flush_on(spdlog::level::trace);
clientLogger->set_level(spdlog::level::trace);
}
}
Entrypoint.h
#pragma once
#ifdef RB_PLATFORM_WINDOWS
extern Ruby::Application* Ruby::CreateApplication();
int main(int argc, char** argv)
{
Ruby::Logger::Init("../Logs/Recent_Run.txt");
RB_CORE_INFO("Initialized the logger.");
auto app = Ruby::CreateApplication();
app->Run();
delete app;
return 0;
}
#else
#error Ruby only supports windows
#endif // RB_PLATFORM_WINDOWS
For anyone else who runs into a similar problem, here is how I fixed it.
Essentially the function signature for the Init() function was the problem. The std::string parameter was causing the debug assertion to fire, my best guess as of right now was because of move semantics but that part I am still not sure on. So there are a couple of ways that I found to fix this.
Method 1:
Make the parameter a const char*. I don't quite like this approach as it then relies on C style strings and if you are trying to write a program in modern C++, this is a huge step backwards.
Method 2:
Make the parameter a const std::string&. Making it a const reference to a string prevents the move semantics (again as far as I know) and the assertion no longer fires. I prefer this fix as it keeps the program in modern C++.
I hope this helps anyone who has similar issues, and be careful with statics and move semantics.

VSCode: Set C/C++ preprocessor macro for static analysis

I am developing a library which lets user set a crucial type alias, or do it through preprocessor directives.
This type alias (or the directive) is undeclared in the library, by design. Thus, when developing my code I get annoying error messages and squiggles for this undeclared type. This could be avoided if I declare a temporary type for it somewhere. However, I do not like the idea of declaring it when I work with the code and then remove it when I publish it. It is also bug prone, since I could easily forget to remove it.
My question is: Can I define preprocessor directives for VS Code's static analysis (IntelliSense? C/C++ Extension)?
That would let me consider the analysis like what a well defined type alias would produce. And avoid annoying error messages/squiggles.
Minimal reproducable example:
Online compiler example
tCore.hpp
#pragma once
#include <string>
// User is responsible of declaring the tCore type
// tCore interface methods
template<typename TCore>
std::string printImpl();
tPrint.hpp
#pragma once
#include <iostream>
class tPrint {
public:
tPrint() = default;
void print() const {
std::cout << printImpl<tCore>() << std::endl; // <-- Static analysis error here!
}
};
tFoo.hpp - tCore candidate
#pragma once
#include <string>
#include "tCore.hpp"
struct tFoo {};
template<>
std::string printImpl<tFoo>() {
return "tFoo";
}
main.cpp
#include <tFoo.hpp>
using tCore = tFoo;
int main() {
tPrint p{};
p.print(); // -> "tFoo"
return 0;
}
I found out it was IntelliSense causing the error through the C/C++ Extension.
I also found an option of adding compiler arguments to IntelliSense, which is exactly what I was looking for.
Either through the UI:
Press
F1 -> > C/C++: Edit Configurations (UI) -> Scroll down to Defines
Or via the JSON :
c_cpp_properties.json configurations has a field defines which holds any compiler arguments.

Why (or when) does a template bring in its namespace?

Here's an example:
#define MAKE_IT_WORK false
namespace Bob { // Bob's project namespace
struct DeviceFrequency {};
extern void debugf(const char* fmt, ...);
} // namespace Bob
namespace SSN { // Super-Secret Namespace
namespace debugging {
extern int ssn_debug;
extern void debugf(const char* fmt, ...);
} // namespace debugging
} // namespace SSN
namespace SSN::signals { // Super-Secret Namespace, project signals
template<typename Coder> // In the example, this imports Bob's namespace
class Frequency {
public:
Frequency( void )
{ using namespace ::SSN::debugging; // Why isn't this enough??
using ::SSN::debugging::debugf; // Or this??
if( ssn_debug )
#if MAKE_IT_WORK
::SSN::debugging:: // How can a developer predict that this is needed??
#endif
debugf("Frequency(%p,%zd)::Frequency\n", this, sizeof(*this));
}
}; // class Frequency
} // namespace SSN::signals
struct Controller {
SSN::signals::Frequency<Bob::DeviceFrequency> bobcon;
Controller( void ) : bobcon() {}
}; // class Controller
In this example, Bob duplicates the debugf function because he doesn't want to have to bring the entire SSN namespace into his private one and doesn't want to be bothered with fully qualifying it every time he uses it.
The SSN developer didn't realize that a template could also import its namespace (until it happened), apparently bringing ADL lookup into play. Although ADL lookup states that using statements in namespace are ignored, why it comes into play at all doesn't make sense. It seems way too easy for one namespace to unintentionally pollute another, and way too hard to predict where this might someday occur in inline code.
It looks like (at least) whenever templates are used within a namespace, every namespace function reference must be fully qualified because when using templates you can't predict when a name conflict might otherwise occur. Is this correct? If so, is there any way to avoid all that extra name qualifier typing that seems to be required? Is this exposure limited to templates, or is all imported inline code somehow similarly vulnerable?
Compiling (Dirty.cpp) using gcc versions 10.2.0 and 10.2.1 (Red Hat 10.2.1-5), I got the following messages:
make dirty
c++ -o Dirty.o -c S/Dirty.cpp -D_CC_GCC -D_OS_BSD -D_HW_X86 -D_OS_LINUX -IS -IH -g -O3 -finline->functions -std=gnu++17 -Wall -Wextra -Wmissing-declarations -Wswitch-default -Werror
S/Dirty.cpp: In instantiation of ‘SSN::signals::Frequency<Coder>::Frequency() [with Coder = Bob::DeviceFrequency]’:
S/Dirty.cpp:146:32: required from here
S/Dirty.cpp:139:12: error: call of overloaded ‘debugf(const char [30], SSN::signals::Frequency<Bob::DeviceFrequency>*, long unsigned int)’ is ambiguous
139 | debugf("Frequency(%p,%zd)::Frequency\n", this, sizeof(*this));
| ~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
S/Dirty.cpp:124:13: note: candidate: ‘void SSN::debugging::debugf(const char*, ...)’
124 | extern void debugf(const char* fmt, ...);
| ^~~~~~
S/Dirty.cpp:117:13: note: candidate: ‘void Bob::debugf(const char*, ...)’
117 | extern void debugf(const char* fmt, ...);
|
(Edited: The example has been restored to the original, now as reformatted by #1201ProgramAlarm. A modified version appears that tests examples follows in the partial answer below.)
For a template, ADL includes the namespaces and entities associated with the
types of the template arguments provided for template type parameters. Because the call to debugf includes this as a parameter, ADL will include namespace Bob because the template is instantiated with Bob::DeviceFrequency.
Updated example:
// Last change: 2020/10/30 11:15 EDT
#define MAKE_IT_WORK true // Modified (Example compiles)
namespace Bob { // Bob's project namespace
extern double thing; // (Added for answer testing)
struct Bar { int foo; }; // (Added for answer testing)
struct Foo { int bar; }; // (Added for answer testing)
extern Foo bar; // (Added for answer testing)
struct DeviceFrequency { double f; }; // (Modified: added "double f;")
extern void debugf(const char* fmt, ...);
static inline void function(DeviceFrequency& E) // (??TRICKY inference??)
{ debugf("default function, E: %f\n", E); }
} // namespace Bob
namespace SSN { // Super-Secret Namespace
namespace debugging {
extern int ssn_debug;
extern void debugf(const char* fmt, ...);
} // namespace debugging
} // namespace SSN
namespace SSN::signals { // Super-Secret Namespace, project signals
template<typename Coder>
class Frequency {
// Note: The Function type references the template indirectly, and has
// no purpose other than to make sure that a <Coder> function handler
// receives a <Coder&> parameter. ADL doesn't care. It brings in the
// Coder's namespace looking for functions to override.
typedef std::function<void(Coder&)> Function; // (Added) Another "gotcha"
const Function function; // This indirectly references Coder&, thus Bob
double thing= 543.21; // (Added) Duplicates name in namespace Bob
double f= 123.45; // (Added) Duplicates name in Bob::DeviceFrequency
Bob::Foo bar{876}; // (Added) Uses Bob namespace
struct Bar { int foo= 0x0F00; } foo; // (Added) Duplicates a Bob struct name
struct Derived : public Bob::Foo {}; // (Added) Uses Bob namespace
Derived secret{{671}}; // (Added) Uses Bob namespace in derived object
struct Container { int n= 888; Bob::Bar m{999}; }; // (Added) Uses Bob namespace
Container content; // (Added) Uses Bob namespace in container object
public:
// This constructor added to demonstrate indirect template references
Frequency( const Function& _function ) : function(_function)
{ using namespace ::SSN::debugging; // Without this, Bob::debugf is used
::SSN::debugging:: // Disambiguate
debugf("Frequency::Frequency(Coder: %p,%zd)\n", &_function, sizeof(_function));
// Drive the function, mostly just to see that it's been coded correctly
// Note the existence of function(DeviceFrequency& E) in namespace Bob.
// This *looks* like it might call Bob::function, but it doesn't,
// likely because function is an object containing an operator(), a
// std::function container.
Coder coder{2468}; function(coder); // (??TRICKY?? Not ADL ambiguous)
Coder rodoc{8642}; _function(rodoc); // (??TRICKY?? Not ADL ambiguous)
Bob::function(coder); // Just to verify it's different
}
Frequency( void ) // Default constructor
{ using namespace ::SSN::debugging; // Why isn't this enough??
using ::SSN::debugging::debugf; // Or this??
// Answer: When ADL comes into play, using statements are ignored.
// Also, without the first using, Bob::debugf would be used.
// (Rationale unclear, but that's the way it is.)
if( ssn_debug )
#if MAKE_IT_WORK
// As explained by #1201ProgramAlarm, it's the 'this' parameter that
// brought Bob's namespace into play for ADL name resolution.
// (Rationale unclear, but that's the way it is.)
::SSN::debugging:: // How can a developer predict that this is needed??
#endif
debugf("Frequency(%p,%zd)::Frequency\n", this, sizeof(*this));
// All remaining lines in Frequency added for detailed testing ============
const void* const that= this; // Another work-around
debugf("Frequency(%p,%zd)::Frequency\n", that, sizeof(*this)); // (Works)
Coder coder{3.14}; // (This is a Bob::DeviceFrequency)
debugf("Coder.f: %f\n", coder.f); // (No ambiguity)
Bob::debugf("Coder: %f\n", coder); // *** AMBIGUOUS debugf ***
debugf("f: %f\n", f); // (No ambiguity)
debugf("thing: %f\n", thing); // (No ambiguity)
debugf("Bob::thing: %f\n", Bob::thing); // (No ambiguity)
debugf("bar.bar: %d\n", bar.bar); // (No ambiguity)
SSN::debugging::debugf("bar: %d\n", bar); // *** AMBIGUOUS debugf ***
Bob::debugf("this->bar: %d\n", this->bar); // *** AMBIGUOUS debugf ***
debugf("foo.foo: 0x%3x\n", foo.foo); // (No ambiguity)
debugf("this->foo: 0x%3x\n", this->foo); // (No ambiguity)
debugf("Bob::bar.bar: %d\n", Bob::bar.bar); // (No ambiguity)
Bob::debugf("Bob::bar: %d\n", Bob::bar); // *** AMBIGUOUS debugf ***
debugf("secret.bar: %d\n", secret.bar); // (No ambiguity)
Bob::debugf("secret: %d\n", secret); // *** AMBIGUOUS debugf ***
debugf("content: %d\n", content); // (No ambiguity)
Bob::debugf("content.m: %d\n", content.m); // *** AMBIGUOUS debugf ***
// End of added lines =====================================================
}
}; // class Frequency
template<typename Coder>
struct Ugly_fix { // Macros stop ADL lookup, remove usings
#define debugf ::SSN::debugging::debugf
#define ssn_debug ::SSN::debugging::ssn_debug
Ugly_fix( void ) // Default constructor
{ if( ssn_debug ) debugf("Ugly_fix(%p)::Ugly_fix\n", this);
debugf("Bob::bar: %d\n", Bob::bar);
// Bob::debugf("Bob::bar: %d\n", Bob::bar); // Syntax error
}
#undef debugf // Macros (MUST BE) private
#undef ssn_debug
}; // struct Ugly_fix
class Non_template { public:
Non_template( void )
{ using namespace ::SSN::debugging;
if( ssn_debug )
debugf("Non_template(%p,%zd)::Non_template\n", this, sizeof(*this));
}
}; // class Non_template
} // namespace SSN::signals
// Namespace: **NONE**
static struct Handler { // Note: This actually *USES* ADL to find debugf
void operator()(Bob::DeviceFrequency& E) { debugf("E: %f\n", E); }
} handler; // struct Handler
struct Controller {
SSN::signals::Frequency<Bob::DeviceFrequency> bobcon;
SSN::signals::Frequency<Bob::DeviceFrequency> robcon;
SSN::signals::Ugly_fix<Bob::DeviceFrequency> ugly;
SSN::signals::Non_template non_template;
// Note that both Frequency constructors are used.
Controller( void ) : bobcon(), robcon(handler), ugly(), non_template() {}
}; // class Controller
// IMPLEMENTATION ============================================================
namespace SSN {
int debugging::ssn_debug= true;
namespace debugging {
void debugf(const char* fmt, ...) {
printf("SSN: "); // (For output disambiguation)
va_list argptr; va_start(argptr, fmt); vprintf(fmt, argptr); va_end(argptr);
} // debugf
} // namespace debugging
} // namespace SSN
namespace Bob {
double thing= 1122.33; // (Added for answer testing)
Foo bar{732}; // (Added for answer testing)
void debugf(const char* fmt, ...) {
printf("Bob: "); // (For output disambiguation)
va_list argptr; va_start(argptr, fmt); vprintf(fmt, argptr); va_end(argptr);
} // debugf
} // namespace Bob
// TEST ======================================================================
static inline int // Number of errors encountered
test_namespace_glitch( void ) // Test namespace glitch
{
int errorCount= 0; // Number of errors encountered
printf("\ntest_namespace_glitch\n");
Controller controller; // (Actually drive the constructor)
if( true ) {
errorCount++; // *Why* does 'this' import namespace?
printf("Still (partially) unexplained\n");
}
return errorCount;
}
(For those who like to run code, most of the test wrappers are also included here. You only need a main program that invokes the test.)
Here are the things I know I need to look out for:
Usage of this as a (bare) parameter inside of template code. (Thanks to #1201ProgramAlarm for pointing this "feature" out.) It's easy to do this accidentally, but hard to figure out why it happened.
Reference of an (uncontrolled) namespace named object (i.e. a struct or a class) used as a parameter to a function whether or not it's inside of template code. This is harder to do accidentally.
This reference can be indirect, as in the Frequency(const Function&) constructor, and therefore more prone to accidental use.
This may or may not be a complete list of ADL "gotcha's." There's a lot of examples in https://en.cppreference.com/w/cpp/language/adl that make me dizzy reading them, but the working ones don't usually seem to be things you might do accidentally once you've been bitten by ADL.
In my opinion, ADL shouldn't occur without explicit intent. Some language keyword or signal like [[adl]]std::swap(obj1,obj2) should be needed to activate it. But that's just me, I guess.
I don't know or understand the rationales behind the ADL design decisions to ignore using statements and to consider a bare this parameter a reference to a template object. I'm sure (OK, hoping) they exist. You may be interested in Andrew Koenig's short commentary A Personal Note About Argument-Dependent Lookup, found while diving into the mysteries of ADL in cppreference.com above.
Also, for the record, I'm up-voting and accepting #1201ProgramAlarm's answer because he explained that the this parameter brought ADL into play. When looking explicitly for it, I didn't find any documentation saying this would happen. While it doesn't answer my question, "How do I prevent this from happening?" I couldn't have gotten started without it.
What does help is that you get a seemingly unexplainable "ambiguous reference" error message that let's you know something strange and bad happened. Too late and too bad for whoever has to fix the problem. Too bad for someone who doesn't think about ADL, and might have to spend an inordinate amount of time first trying to isolate the problem and even then finally have to call on stackoverflow for help because ADL's just so weird.
I have an ugly way to bypass ADL, shown in the Ugly_fix constructor. By using (ugh) macros to auto-magically fully qualify names, not only is ADL prevented but the using statements are no longer needed. This kind of macro must be temporary, undefined after use, to avoid macro namespace pollution.

Registering file handlers in code running prior to main()

I'm looking into ways to prevent unnecessary clutter in setup code in main() as well as various other places. I often have tons of setup code that registers itself with some factory. A standard example is e.g. handlers for various file types.
To avoid having to write this code and instead just make handlers magically work if linked into the application, I figured I could replace the code by something like the following:
test.cc:
int main() {
return 0;
}
loader.h:
#ifndef LOADER_H_
#define LOADER_H_
#include <functional>
namespace loader {
class Loader {
public:
Loader(std::function<void()> f);
};
} // namespace loader
#define REGISTER_HANDLER(name, f) \
namespace { \
::loader::Loader _macro_internal_ ## name(f); \
}
#endif // LOADER_H_
loader.cc:
#include "loader.h"
#include <iostream>
namespace loader {
Loader::Loader(std::function<void()> f) { f(); }
} // namespace loader
a.cc:
#include <iostream>
#include "loader.h"
REGISTER_HANDLER(a, []() {
std::cout << "hello from a" << std::endl;
})
The idea here is that a.cc would in a real application e.g. call some method where it registers it self as a handler for a certain file type. Compiling the code with c++ -std=c++11 test.cc loader.cc a.cc creates a binary that prints "hello from a" while c++ -std=c++11 test.cc loader.cc stays silent.
I'm wondering if there's something subtle that I might need to be careful with? For example, if someone creates complex objects in the lambda that is run here, I assume weird things can happen during cleanup for example in a multithreaded application?
You wrote:
... unnecessary clutter in setup code in main() ...
int main() {
return 0;
}
This is not preventing unnecessary clutter. This is hiding your initializations. They still occur, but now you have to chase after them. That's really not the way to do it. Also, it will force the use of a lot of global state - in many independent global variables, most probably - which is also a bad thing. Instead, consider writing something like:
class my_app_state { /* ... */ };
my_app_state initialize(/* perhaps with argc and argv here? */) {
//
// Your "unnecessary" clutter goes here...
//
return whatever;
}
int main() {
auto app_state = initialize();
//
// do stuff involving the app_state...
//
}
and don't try to "game" the program loader.
This approach is not guaranteed to work:
[basic.start.dynamic]/4 It is implementation-defined whether the dynamic initialization of a non-local non-inline variable with static storage duration is sequenced before the first statement of main or is deferred. If it is deferred, it strongly happens before any non-initialization odr-use of any non-inline function or non-inline variable defined in the same translation unit as the variable to be initialized.
Thus, the initialization of _macro_internal_a may be deferred until something in a.cc is used. And since nothing in a.cc is in fact used, the initialization may not be performed at all.
In practice, linkers tend to discard object files that do not appear to be referenced by anything in the program (especially when those files come from libraries).

how do I set a debug mode in c++

I would like to set a debug mode so that it prints the log statements only if the debug mode is on. For example if I have code like this
printf("something \n");
.
.
.
perror("something \n");
It only works if the debug flag is on.. I don't want to use "if" statements.
I think there is a clever way to do this using #define or something..
Thank is advance..
#ifdef _DEBUG // or #ifndef NDEBUG
#define LOG_MSG(...) printf(__VA_ARGS__) // Or simply LOG_MSG(msg) printf(msg)
#else
#define LOG_MSG(...) // Or LOG_MSG(msg)
#endif
On non-Debug built LOG_MSG would yeild to nothing. Instead of defining it with raw printf, you can have your custom logging-function, or class-method to be called.
Without going in to specific libraries or solutions, generally people make a logger class or function, and a single debug flag. The debug function checks this flag before calling printf or cout. Then in the rest of your code you simply call your debug function / method.
Here's an example:
class MyDebugger
{
private:
bool m_debug;
public:
MyDebugger();
void setDebug(bool debug);
void debug(const char* message);
};
MyDebugger::MyDebugger()
{
m_debug = false;
}
void MyDebugger::setDebug(bool debug)
{
m_debug = debug;
}
void MyDebugger::debug(const char* message)
{
if(m_debug)
{
cout << message << endl;
}
}
int main(int argc, char** argv)
{
MyDebugger debugger;
debugger.debug("This won't be shown");
debugger.setDebug(true);
debugger.debug("But this will");
return 0;
}
of course this is an incredibly naive implementation. In real logger classes there are many levels for finer-grained control of how much detail gets printed (levels like error, warning, info, and debug to differentiate the importance of the message). They might also let you log to files as well as stdout. Still this should give you a general idea.
In GCC, something like
#define debugprint(...) printf(__VA_ARGS__)
You can do a simple C-style macro definition (especially if you compiler is modern enough to do variable arguments macros, i.e. gcc or VS2005+) doing printf with a check of the debug level which can be a static global variable.
If you go with C++-style class similar to what #Chris suggests, I would make the logging function inline to ensure that when logging is disabled you are not wasting time on calling functions.