Suppose I have a C++ class that uses conditional compilation:
C.hpp
namespace NS{
class C {
public:
C(void);
~C(void);
int func( int arg1 );
private:
int memberVar;
}
C.cpp:
#include "C.hpp"
namespace NS{
C::C( void ){ memberVar = 0; }
C::~C( void ) {}
int C::func( int arg1 ){
int retval = 0;
memberVar = arg1;
#ifdef DEV_BUILD
retval = memberVar;
printf( "memberVar was set.\n" );
#endif
return retval;
}
}
(this is a simplified example; imagine that the class C is several hundred lines long, with some of its functions using conditional compilation, each using the same condition: #ifdef DEV_BUILD. The application is an embedded-systems application, and the condition is used to distinguish between test hardware and production hardware, which have some differences)
I've been advised to instead implement the funcs in separate .cpp files... but I'm sort of hung up as to what the best way is.
My first impulse was to create C_dev.cpp and C_prod.cpp, implement C::func() differently in each, and edit C.cpp thusly:
#include "C.hpp"
#ifdef DEV_BUILD
#include "C_dev.cpp"
#else
#include "C_prod.cpp"
#endif
namespace NS{
C::C( void ){ memberVar = 0; }
C::~C( void ) {}
}
...but is this bad style? Or otherwise problematic?
Additional restrictions:
Can't use subclasses.
Can't use template classes.
(Update: Using separate files was suggested, but not required. I'm open to other suggestions)
Big point number 1: If you have hundreds of minor changes scattered all over the place in a sea of otherwise identical code, go with the conditional compilation. It will usually be easier to read and maintain.
But if you have differences broken down at the function level, you can split the different functions among the different implementation files. Usually I'm doing this with operating system wrappers, and the differences in, say, getting a directory list on Linux and Windows are pretty coarse-grained. (Note: This example has been rendered obsolete by C++17)
Big point number 2: Don't make multiple headers. This just compounds the problem. You want to expose a common interface between the two implementations. If you can't, you've already lost and need to find a different path. Make a common header that fits all of the implementations.
For this answer all implementations use the Asker's original header, C.hpp:
namespace NS{
class C {
public:
C(void);
~C(void);
int func( int arg1 );
private:
int memberVar;
}
I cut the Asker's example up into three cpp files: One with common functionality across all of the builds and two implementing the the functions containing differences. This way you aren't repeating any functions you don't absolutely need to repeat.
C_common.cpp All of the shared functionality goes here
#include "C.hpp"
namespace NS{
C::C( void ){ memberVar = 0; }
C::~C( void ) {}
}
C_debug.cpp: functions with debug statements go here
#include "C.hpp"
namespace NS{
int C::func( int arg1 ){
int retval = 0;
memberVar = arg1;
retval = memberVar;
printf( "memberVar was set.\n" );
return retval;
}
}
C_no_debug.cpp: functions without debug statements go here
#include "C.hpp"
namespace NS{
int C::func( int arg1 ){
memberVar = arg1;
return memberVar;
}
}
When you link the program, you always link C_common and specify which of C_debug and C_no_debug to link.
You can sometimes take this a step further by breaking down complex functions and isolating only the differences and giving the differences a function called from within a shared function.
C_common.cpp
#include "C.hpp"
namespace NS{
C::C( void ){ memberVar = 0; }
C::~C( void ) {}
int C::func( int arg1 ){
memberVar = arg1;
debugstuff(); // calls the different functionality
return memberVar;
}
}
C_debug.cpp:
#include "C.hpp"
namespace NS{
void debugstuff()
{
printf( "memberVar was set.\n" );
}
}
C_no_debug.cpp:
#include "C.hpp"
namespace NS{
void debugstuff()
{
}
}
This tends to scale poorly as you may wind up with many one liner functions. See the Big point number 1 above. But if the differences are shaped just right you can take advantage of passing function parameters to reduce the spam. In this case the logical thing to do is pass in the debug string. One prints and the other discards.
C_common.cpp
#include "C.hpp"
namespace NS{
C::C( void ){ memberVar = 0; }
C::~C( void ) {}
int C::func( int arg1 ){
memberVar = arg1;
debugstuff("memberVar was set.\n"); // calls the different functionality
// and tells it what to do!
return memberVar;
}
}
C_debug.cpp:
#include "C.hpp"
namespace NS{
void debugstuff(const char * message)
{
printf( message );
}
}
C_no_debug.cpp:
#include "C.hpp"
namespace NS{
void debugstuff(const char * )
{
}
}
Without more information I'd suggest that whoever is telling you to use different files is leading you in the wring direction.
Let me address the specific question: ...but is this bad style? Or otherwise problematic?
Yes and Yes.
It is the opposite of what everyone else does which kind of hints that you're doing it wrong. Plus any new developer will have to learn your system's unique "build quirks".
It means you have twice as many files for your build system / software repository to keep track of.
Any bug you find has to be fixed in both files (that's what I meant by keeping files in sync).
You probably need a way to know which version of the source was used to build the exe.
Related
[UPDATE] a reproducible example
By splitting declaration and definition into different files, my toy code built without problem...
my main.cpp
#include <iostream>
#include "file0.h"
#include "file1.h"
#include "file2.h"
int main() {
test a;
double b = a.get_recipocal(2.0);
double c = a.get_sqrt(2.0);
std::cout << "b = " << b << std::endl;
std::cout << "c = " << c << std::endl;
return 0;
}
my file0.h
#ifndef TEST_MULTI_FILES_FILE0_H
#define TEST_MULTI_FILES_FILE0_H
class test {
private:
double a;
public:
double get_recipocal(int a);
double get_sqrt(int a);
};
#endif//TEST_MULTI_FILES_FILE0_H
my file1.h
#ifndef TEST_MULTI_FILES_FILE1_H
#define TEST_MULTI_FILES_FILE1_H
double test::get_recipocal(int a) {
return 1. / a;
}
#endif//TEST_MULTI_FILES_FILE1_H
my file2.h
#include <cmath>
#ifndef TEST_MULTI_FILES_FILE2_H
#define TEST_MULTI_FILES_FILE2_H
double test::get_sqrt(int a) {
return sqrt(a);
}
#endif//TEST_MULTI_FILES_FILE2_H
Built and run without problem. So it is not an obvious error in my production code
Thanks!
(below are original question)
I am new to C++ classes, here is my problem
for example, I have a class in file b0.h
class B::virtual public A {
void func1() {
some work1;
}
void func2() {
some work2;
}
...
};
Now I want to move the implementation of func2() into an other header file, say b1.h
so I did the following:
#include "path/to/b0.h"
void B::func2(){
some work2;
}
But the compiler will complain
use of undeclared identifier 'B'
void B::func2() {
^
(I also tested the following: in b0.h, I included b1.h and if I made func2() in b1.h a static function, I can call it from b0.h without problem.)
The goal is to separate members of B into two header files, each one contain one function implementation (I want to have a side-by-side comparison between func1 and func2 in different file, they are similar and very long). I feel this is a very common scenario, but I didn't find a good example. Is there an obvious mistake? Thank you!
What you want to do is quite common if you have larger classes. What you need is one header file with all the declarations, say b.h:
#include "path/to/a.h"
class B : virtual public A { // : instead of ::
void func1(); // return type, no function body
void func2(); // return type, no function body
};
Then you can split the definitions (the actual implementation) over as many source files as you wish. For example file b_func1.cpp might contain the definition for B::func1
#include "path/to/b.h"
void B::func1() { /* some code */ }
And file b_func2.cpp might contain the definition for B::func2
#include "path/to/b.h"
void B::func2() { /* some code */ }
Anyway, only in specific cases (like templates or inline functions) the definition can be in a header. It may work as long as you include every header only once, but it's still wrong, so put the definitions in a translation unit / source file.
I have several c++ programs that are all reading a YAML configuration file in /etc/foo/config.yml. I have written a function that reads the config from the file
YAML::Node load_config();
(using the yaml-cpp library).
I would like this configuration to be loaded once, at the beginning of the main() function of my program, and then accessible everywhere as some kind of global variable.
Currently, many of my functions have extra parameters that are just values read from the configuration file. It could be avoided by having this global configuration, making my function definitions and calls much simpler and readable.
Side note: I am also using OpenMP for distributing computation, which means that the configuration must be accessible to all parallel processes.
Could someone give a tiny example of what this would look like when done the right way?
Thanks!
here's one way. It's a variation on the idea of the schwartz counter to manage a global singleton (for example, std::cout itself)
// globals.hpp
#include <istream>
struct globals_object
{
globals_object()
{
// record number of source files which instanciate a globals_object
++init_count_;
}
~globals_object()
{
// The last source file cleans up at program exit
if(--init_count_ == 0)
{
if (pimpl_)
{
delete pimpl_;
}
}
}
// internal implementation
struct impl
{
void load(std::istream& is)
{
// do loading code here
}
int get_param_a() const {
return a_;
}
int a_;
};
// (re)load global state
void load(std::istream&& is)
{
if (pimpl_) delete pimpl_;
pimpl_ = new impl;
pimpl_->load(is);
}
// public parameter accessor
int get_param_a() const {
return get_impl().get_param_a();
}
private:
static int init_count_;
static impl* pimpl_;
static impl& get_impl()
{
return *pimpl_;
}
};
// one of these per translation unit
static globals_object globals;
// globals.cpp
// note - not initialised - will be zero-initialised
// before global constructors are called
// you need one of these in a cpp file
int globals_object::init_count_;
globals_object::impl* globals_object::pimpl_;
// main file
// #include "globals.hpp"
#include <fstream>
int main()
{
globals.load(std::ifstream("settings.yml"));
}
// any other file
// #include "globals.hpp"
#include <iostream>
void foo()
{
std::cout << globals.get_param_a() << std::endl;
}
The goal is to allow header files to "register" an initializer function so that main can just iterate over those functions and call them. I've stumbled upon a solution which uses __attribute__, but it seems to be GCC-only (https://stackoverflow.com/a/37082249/7867841).
// header1.h
void myInitializer(){}
REGISTER_THIS(&myInitializer);
// header2.h
void myInitializer2(){}
REGISTER_THIS(&myInitializer2);
// main.cpp
...
for_each_registered_ptr(){ call_ptr(); } // calls myInitializer and myInitializer2
...
Is there a universal solution to this? Functions can be switched with classes or types if that's easier to implement.
You can abuse static function locals to do this, avoiding the static initialization order fiasco.
In init.h, we have this:
#ifndef INIT_H
#define INIT_H
#include <vector>
// Can be changed to std::function<...> or whatever you need.
typedef void (*init_fn)();
// Returns int so it can easily be used in a variable initializer.
int register_initializer(init_fn fn);
std::vector<init_fn> & get_initializers();
#endif
Then, in init.cpp:
#include "init.h"
int register_initializer(init_fn fn)
{
get_initializers().push_back(fn);
return 0;
}
std::vector<init_fn> & get_initializers()
{
static std::vector<init_fn> ip;
return ip;
}
A few notes, before we move on to the rest:
The static local is only initialized once, the first time the function is called.
The "global" vector is kind-of-leaked. It's unlikely this will be a problem unless you are adding tens of thousands of entries to this vector. You can always get_initializers().clear() to empty it out after using it.
We'd use it like so, in a.cpp:
#include <iostream>
#include "init.h"
static void a_init() { std::cout << "a_init()\n"; }
static auto dummy = register_initializer(a_init);
And, finally, we have our (rather simple) main.cpp:
#include "init.h"
int main() {
for (auto fn : get_initializers()) {
fn();
}
return 0;
}
I have these two files table.cpp and table.h in my program code apart from the main.cpp. The files are described as below
table.cpp
#include <iostream>
#include "table.h"
using namespace std;
// accessor function for Name
char* PeriodicTable::Name()
{
return Name;
}
// accessor function for Symbol
char* PeriodicTable::Symbol()
{
return Symbol;
}
table.h
#ifndef TABLE_H
#define TABLE_H
class PeriodicTable
{
char Name[15], Symbol[3], GroupName[20], Block, State[25], Colour[15], Classification[20];
int GroupNo, AtomicNo, PeriodNo;
float Weight;
public:
char* Name();
char* Symbol();
};
#endif
but the problem is that the IntelliSense(since I am using Visual C++ Express 2010) shows a red curved underline below the name and symbol in the accessor function in table.cpp. I can't understand why???
Your member functions and member variables have the same name. This is not possible in C++. That's why various conventions exist for naming member variables, e.g. m_name, name_ etc. (NB: When dealing with underscores in identifiers make sure you don't use a reserved name by accident.)
You might wonder why and how that could possibly go wrong. In your example there clearly is no way to invoke operator() on char[15], but the problem is that the compiler only knows that after performing semantic analysis. There could also be cases where it is impossible to disambiguate. For example:
struct Func {
void operator()() { };
};
struct C {
Func f;
void f() {}
};
int main() {
C c;
c.f(); // which one?
}
My current implementation, simplified:
#include <string>
#include <memory>
class Log
{
public:
~Log() {
// closing file-descriptors, etc...
}
static void LogMsg( const std::string& msg )
{
static std::unique_ptr<Log> g_singleton;
if ( !g_singleton.get() )
g_singleton.reset( new Log );
g_singleton->logMsg( msg );
}
private:
Log() { }
void logMsg( const std::string& msg ) {
// do work
}
};
In general, I am satisfied with this implementation because:
lazy instantiation means I don't pay unless I use it
use of unique_ptr means automatic cleanup so valgrind is happy
relatively simple, easy-to-understand implementation
However, the negatives are:
singletons aren't conducive to unit-testing
dissonance in the back of my mind for introducing a pseudo-global (a bit of a code smell)
So here are my questions directed towards those developers who are successful in exorcising all singletons from their C++ code:
What kind of non-Singleton implementation do you use for application-wide logging?
Is the interface as simple and accessible as a Log::LogMsg() call above?
I want to avoid passing a Log instance all over my code, if at all possible - note: I am asking because, I, too, want to exorcise all Singletons from my code if there is a good, reasonable alternative.
First: the use of std::unique_ptr is unnecessary:
void Log::LogMsg(std::string const& s) {
static Log L;
L.log(s);
}
Produces exactly the same lazy initialization and cleanup semantics without introducing all the syntax noise (and redundant test).
Now that is out of the way...
Your class is extremely simple. You might want to build a slightly more complicated version, typical requirements for log messages are:
timestamp
level
file
line
function
process name / thread id (if relevant)
on top of the message itself.
As such, it is perfectly conceivable to have several objects with different parameters:
// LogSink is a backend consuming preformatted messages
// there can be several different instances depending on where
// to send the data
class Logger {
public:
Logger(Level l, LogSink& ls);
void operator()(std::string const& message,
char const* function,
char const* file,
int line);
private:
Level _level;
LogSink& _sink;
};
And you usually wrap the access inside a macro for convenience:
#define LOG(Logger_, Message_) \
Logger_( \
static_cast<std::ostringstream&>( \
std::ostringstream().flush() << Message_ \
).str(), \
__FUNCTION__, \
__FILE__, \
__LINE__ \
);
Now, we can create a simple verbose logger:
Logger& Debug() {
static Logger logger(Level::Debug, Console);
return logger;
}
#ifdef NDEBUG
# define LOG_DEBUG(_) do {} while(0)
#else
# define LOG_DEBUG(Message_) LOG(Debug(), Message_)
#endif
And use it conveniently:
int foo(int a, int b) {
int result = a + b;
LOG_DEBUG("a = " << a << ", b = " << b << " --> result = " << result)
return result;
}
The purpose of this rant ? Not all that is a global need be unique. The uniqueness of Singletons is generally useless.
Note: if the bit of magic involving std::ostringstream scares you, this is normal, see this question
I'd go with the simple, pragmatic solution:
you want a solution that is globally accessible. For the most part, I try to avoid globals, but for loggers, let's face it, it's usually impractical.
So, we do need something to be globally accessible.
But, we don't want the additional "there can be only one" restriction that a singleton confers. Some of your unit tests might want to instantiate their own private logger. Others might want to replace the global logger, perhaps.
So make it a global. A plain old simple global variable.
This still doesn't fully solve the problem with unit testing, admittedly, but we can't always have everything we want. ;)
As pointed out in the comment, you need to consider the initialization order for globals, which, in C++, is partly undefined.
In my code, that is generally not a problem, because I rarely have more than one global (my logger), and I stick rigidly to a rule of never allowing globals to depend on each others.
But it's something you have to consider, at least.
I really like the following interface since it uses streaming. Of course you can add channels, time and thread information to it. Another possible extension is to use the __FILE__ and __LINE__ macros and add it as parameters to the constructor. You could even add a variadic template function if you do not like the stream syntax. If you want to store some configuration you could add them to some static variables.
#include <iostream>
#include <sstream>
class LogLine {
public:
LogLine(std::ostream& out = std::cout) : m_Out(out) {}
~LogLine() {
m_Stream << "\n";
m_Out << m_Stream.rdbuf();
m_Out.flush();
}
template <class T>
LogLine& operator<<(const T& thing) { m_Stream << thing; return *this; }
private:
std::stringstream m_Stream;
std::ostream& m_Out;
//static LogFilter...
};
int main(int argc, char *argv[])
{
LogLine() << "LogLine " << 4 << " the win....";
return 0;
}
// file ILoggerImpl.h
struct ILoggerImpl
{
virtual ~ILoggerImpl() {}
virtual void Info(std::string s) = 0;
virtual void Warning(std::string s) = 0;
virtual void Error(std::string s) = 0;
};
// file logger.h //
#include "ILoggerImpl.h"
class CLogger: public ILoggerImpl
{
public:
CLogger():log(NULL) { }
//interface
void Info(std::string s) {if (NULL==log) return; log->Info(s); }
void Warning(std::string s) {if (NULL==log) return; log->Warning(s); }
void Error(std::string s) {if (NULL==log) return; log->Error(s); }
//
void BindImplementation(ILoggerImpl &ilog) { log = &ilog; }
void UnbindImplementation(){ log = NULL; }
private:
ILoggerImpl *log;
};
// file: loggers.h //
#include "logger.h"
extern CLogger Log1;
extern CLogger Log2;
extern CLogger Log3;
extern CLogger Log4;
extern CLogger LogB;
/// file: A.h //
#include "loggers.h"
class A
{
public:
void foo()
{
Log1.Info("asdhoj");
Log2.Info("asdhoj");
Log3.Info("asdhoj");
}
private:
};
/// file: B.h //
#include "loggers.h"
class B
{
public:
void bar()
{
Log1.Info("asdhoj");
Log2.Info("asdhoj");
LogB.Info("asdhoj");
a.foo();
}
private:
A a;
};
////// file: main.cpp ////////////////
#include "loggers.h"
#include "A.h"
#include "B.h"
#include "fileloger.h"
#include "xmllogger.h"
CLogger Log1;
CLogger Log2;
CLogger Log3;
CLogger Log4;
CLogger LogB;
// client code
int main()
{
std::unique_ptr<ILoggerImpl> filelog1(new CFileLogger("C:\\log1.txt"));
Log1.BindImplementation(*filelog1.get());
std::unique_ptr<ILoggerImpl> xmllogger2(new CXmlLogger("C:\\log2.xml"));
Log2.BindImplementation(*xmllogger2.get());
std::unique_ptr<ILoggerImpl> xmllogger3(new CXmlLogger("C:\\logB.xml"));
LogB.BindImplementation(*xmllogger3.get());
B b;
b.bar();
return 0;
};
// testing code
///////file: test.cpp /////////////////////////////////
#include "loggers.h"
CLogger Log1;
CLogger Log2;
CLogger Log3;
CLogger Log4;
int main()
{
run_all_tests();
}
///////file: test_a.cpp /////////////////////////////////
#include "A.h"
TEST(test1)
{
A a;
}
TEST(test2, A_logs_to_Log1_when_foo_is_called())
{
A a;
std::unique_ptr<ILoggerImpl> filelog1Mock(new CFileLoggerMock("C:\\log1.txt"));
Log1.BindImplementation(*filelog1.get());
EXPECT_CALL(filelog1Mock Info...);
a.foo();
Log1.UnbindImplementation();
}