how to avoid using ifdef to make code more testable - c++

I am creating a log file for logging information. It has a switch to turn it on or off.
#define LOG_FILE
#ifdef LOG_FILE
#define LOG(MESSAGE1, MESSAGE2) log(MESSAGE1, MESSAGE2);
#else
#define LOG(MESSAGE1, MESSAGE2)
#endif
Now I can control logging using LOG_FILE switch. I was wondering there are any other way achieve similar feature which is more testable?

You can use templates like this:
#include <string>
#include <iostream>
#define USE_LOGGING true
template<bool>
inline void log(const std::string& message, const std::string& verbosity) {}
template<>
inline void log<true>(const std::string& message, const std::string& verbosity) {
std::cout << verbosity << ": " << message << "\n";
}
constexpr auto logMessage = log<USE_LOGGING>;
int main(int argc, char** argv) {
logMessage("Test", "Warning");
}
By this you will have both versions available, using logMessage gives you the global settings, alternatively you can use log directly which is a local definition of how to use logging.

Related

How to pass const string parameter from perl to c++ using swig

I am new to Swig and I need help in debugging this error.
My motive is to create a logging system with less time consuming using Swig, C++ , Perl. But i got error while passing the string parameter form Perl to C++ via swig.
I have placed my entire code and error below.
Please help me to solve this issue. Thanks in future.
Logger.cpp
#include <stdexcept>
#include <string>
#include <ctime>
#include "Logger.h"
using namespace std;
Logger* Logger::pInstance = nullptr;
Logger& Logger::instance()
{
lock_guard<mutex> guard(sMutex);
printf("instance");
if (pInstance == nullptr)
pInstance = new Logger();
return *pInstance;
}
void Logger::Cleanup()
{
lock_guard<mutex> guard(Logger::sMutex);
delete Logger::pInstance;
Logger::pInstance = nullptr;
}
Logger::Logger()
{
printf("Constructor\n");
mOutputStream.open("log.out", ios_base::app);
if (!mOutputStream.good()) {
throw runtime_error("Unable to initialize the Logger!");
}
}
void Logger::log(const string& inMessage, const string& inLogLevel)
{
lock_guard<mutex> guard(sMutex);
logHelper(inMessage, inLogLevel);
}
void Logger::logHelper(const std::string& inMessage, const std::string& inLogLevel)
{
mOutputStream << "[ " << current_time() << " ]" << inLogLevel << ": " << inMessage << endl;
}
string Logger::current_time()
{
time_t rawtime;
struct tm * timeinfo;
char buffer[80];
time (&rawtime);
timeinfo = localtime(&rawtime);
strftime(buffer,sizeof(buffer),"%d-%m-%Y %H:%M:%S",timeinfo);
std::string str(buffer);
return str;
}
Logger::~Logger(){
printf("deconstructor\n");
mOutputStream.close();
}
Logger.h
#ifndef LOGGER_H_
#define LOGGER_H_
#include <stdio.h>
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <mutex>
class Logger
{
public:
Logger();
~Logger();
Logger& instance();
void log(const std::string& inMessage,
const std::string& inLogLevel);
protected:
static Logger* pInstance;
std::ofstream mOutputStream;
void Cleanup();
void logHelper(const std::string& inMessage,
const std::string& inLogLevel);
std::string current_time();
private:
std::mutex sMutex;
};
#endif
Logger.i
%module Logger
%{
#include "Logger.h"
%}
%include "Logger.h"
ch.pl
use strict;
use warnings;
use Cwd qw( abs_path );
use File::Basename qw( dirname );
use lib dirname(abs_path($0));
use Logger;
my $p=new Logger::Logger();
$p->instance();
$p->log("test message");
Error message*
TypeError in method 'Logger_log', argument 2 of type 'std::string' at ch1.perl line 19.
You need to include a typemap for std::string in your interface file so SWIG knows how to handle the type, see chapter 9.4 in the manual:
9.4.1 std::string The std_string.i library provides typemaps for converting C++ std::string objects to and from strings in the target
scripting language.
%module Logger
%include "std_string.i"
%{
#include "Logger.h"
%}
%include "Logger.h"
If I use this and run ch.pl like this:
use strict;
use warnings;
use lib '.';
use Logger;
my $p = Logger::Logger->new();
$p->instance();
$p->log("test message", "DEBUG");
I get output:
Constructor
instanceConstructor
deconstructor
The content of the log.out file is:
[ 22-05-2020 17:35:12 ]DEBUG: test message

How do I get values from a struct in a different cpp file?

I think there are many solutions outside for my problem but I dont get it, I'm kind of new to structs - so please help me..
OK my problem is I declare a struct in my header.h file and there is a function also inside that puts a string in one of the struct values and in the header file I can also output the string, but I want that struct and that !!value!! in a different cpp file where I can access to that value - so here is my code
header.h
#include <iostream>
#include <string.h>
#ifndef FUNCTIONS_H
#define FUNCTIONS_H
struct FUNCTIONS
{
std::string f_name;
};
//extern FUNCTIONS globalStruct;
//put in struct variable
void put2struct()
{
struct FUNCTIONS struct1;
struct1.f_name = "FUNCTION";
std::cout << "Functionname: " << struct1.f_name << std::endl;
}
#endif //FUNCTIONS_H
and main.cpp
#include <iostream>
#include <string.h>
#include "header.h"
using namespace std;
int main(int argc, char const *argv[])
{
struct FUNCTIONS globalStruct;
put2struct();
//FUNCTIONS struct1;
std::cout << "Functionname2: " << globalStruct.f_name << std::endl;
return 0;
}
I hope somebody can help me I really dont get it how to do this :/
There is no way to directly access a local variable outside the block where it is defined. Because struct1 is an automatic variable, it is destroyed when put2struct returns, and no longer exists after that.
You can write a function that takes a FUNCTIONS by reference, and modify put2struct to call that function. That way you can access struct1 from a different cpp file:
void foo(FUNCTIONS&);
void put2struct()
{
FUNCTIONS struct1;
// do your thing
foo(struct1);
}
// another file
void foo(FUNCTIONS& object) {
// you have access to the object passed by reference
}

'DemoProject::Logger' : 'class' type redefinition

I've read a lot of questions regarding this matter, but none of them seem to solve my issue. Code below:
Logger.cpp
#include "Includes.h"
namespace DemoProject {
class Logger {
public:
static void Logger::printm(CEGUI::String Message) {
std::cout << currentDateTime() << " >> " << Message << std::endl;
}
private:
static const std::string currentDateTime() {
time_t now = time(0);
struct tm tstruct;
char buf[80];
tstruct = *localtime(&now);
strftime(buf, sizeof(buf), "%d-%m-%Y %X", &tstruct);
return buf;
}
};
}
logger.h
#ifndef LOGGER_H
#define LOGGER_H
#pragma once
#include "Includes.h"
namespace DemoProject {
class Logger {
public:
static void Logger::printm(CEGUI::String Message);
};
}
#endif
Includes.h
#ifndef INCLUDES_H
#define INCLUDES_H
#include <iostream>
#include <string>
#include <stdio.h>
#include <time.h>
#include <CEGUI/CEGUI.h>
#include <CEGUI/RendererModules/OpenGL/GLRenderer.h>
#include <SDL.h>
#include <SDL_opengl.h>
#include "Logger.h"
#endif
Sorry for bad formating of the post, but that's the best I could do. I am mostly a C# developer, but I am trying to learn C++ through doing different exercises I create on my own. From a C# developers view, this code is okay, but I don't know, I'm still a beginner.
There are a couple of things you're doing weird. But the most important is you don't need to declare the class again in the .cpp file. You just implement the functions:
namespace DemoProject {
void Logger::printm(CEGUI::String Message) {
std::cout << currentDateTime() << " >> " << Message << std::endl;
}
static const std::string currentDateTime() {
...
}
}
You also didn't declare currentDateTime in the header, so that won't compile right. You also don't need to scope the class in the declaration since you're already in the class, so your header should look like:
namespace DemoProject {
class Logger {
public:
static void printm(CEGUI::String Message);
static const std::string currentDateTime();
};
}

error: ‘logFileObj’ does not name a type

I have a file named global.h whose contents are:
#define DEPTH 10
#define LOGGING //to log the progress of the program.
#ifdef LOGGING
#include <fstream>
#include <string>
extern std::string logFileName;
extern std::ofstream logFileObj;
#endif
Also main.cpp:
#include "global.h"
using namespace std;
#ifdef LOGGING
string logFileName = ".log";
ofstream logFileObj;
logFileObj.open(logFile); //line 13
logFileObj<<"depth: "<<DEPTH<<endl; //line 14
#endif
I am constantly getting the following error in compilation:
src/main.cpp:13:1: error: ‘logFileObj’ does not name a type
src/main.cpp:14:1: error: ‘logFileObj’ does not name a type
Any help appreciated.
C++ does not allow operation outside function. C++ allows you define variable globally but you need to put operations inside functions.
If I read your question correctly, you just need a function and call it when you need to:
#include <fstream>
#include <utility>
#include <string>
template<typename T>
void WriteLog(const std::string& log_file_name, const std::string& prefix, const T& data)
{
std::ofstream log_file_handler(log_file_name.c_str(), std::ios::app); // if you use C++11, you could use string directly
log_file_handler << prefix << data << std::endl;
}
usage:
WriteLog<int>("app.log", "depth:", DEPTH);

How to comment lines automatically in release mode?

I need to have some lines of code "active" in debug mode only, and ignored in release mode.
Is there a way to do something like this:
#include <iostream>
using namespace std;
#ifdef _TEST_
#define _cerr cerr
#else
#define _cerr // cerr
#endif
int main() {
_cerr << "TEST message" << endl;
}
So that when _TEST_ is not defined, some lines are commented, or removed from the code. I know that comments are processed before the rest, so this code is wrong. But how can I get the behaviour I need without using #ifdefs explicitely?
You can use a macro for this:
#ifdef _TEST_
#define DEBUG_ONLY(x) x;
#else
#define DEBUG_ONLY(x)
#endif
int main() {
DEBUG_ONLY(cerr << "TEST message" << endl)
}
If what you are after is debug logging which is removed in release builds, you can do something like:
#ifdef _TEST_
#define LOG(x) (cerr << x << endl)
#else
#define LOG(x)
#endif
...
int main() {
LOG("TEST message");
}
Use this:
#ifdef _TEST_
#define DEBUG_TEST(x) x
#else
#define DEBUG_TEST(x)
#endif
int main() {
DEBUG_TEST(_cerr << "TEST message" << endl);
}
ifdefs are the way to go. How else would you know if the compiler is in release vs debug mode? When else besides during the preprocessing stage would that be communicated? At what other stage could you decide to remove/add code (besides during template generation). Hey maybe we can use template generation... but you still have to key off the ifdef somehow to control your template.
Maybe there's a really slick way to do this that I'm not thinking of, but everyone knows/uses ifdefs for this purpose. If you throw them a curveball its only going to drastically increase the human cost of maintaining your code.
Stick with ifdefs.
This here basically does what you are asking for:
#ifdef _TEST_
#define _cerr cerr
#else
#define _cerr if (1) {} else cerr
#endif
But don't be surprised if you for example get compiler warnings about ambiguous else if you write something like this:
if (something)
_cerr << "Why?" << std::endl;
You should always be aware of the fact that this _cerr is in fact a non-trivial macro.
Defining _cerr to nothing will fail the compilation. You could instead define a macro that you exclude while in release mode.
For example:
#ifdef _TEST_
#define LOG_ERROR(log) cerr << log << endl;
#else
#define LOG_ERROR(log)
#endif
Then in your code:
int main() {
LOG_ERROR("TEST message");
}
int main() {
#ifdef _TEST_
_cerr << "TEST message" << endl;
#endif
}
No. Absolutely, no.
Try a variation on this:
#ifdef _TEST_
ostream& _cerr = cerr;
#else
ostringstream _cerr;
#endif
(Basically you would want a stream which just discards its input.)
A nicer solution for the "no logging in release" is the following class:
class NullStream {
template<typename T> NullStream& operator<< const(T&) { }
};
Use:
#ifdef DEBUG
#define CERR std::cerr
#else
#define CERR NullStream()
#endif
Create your own NULL stream.
#include <iostream>
class NullStream {};
template<typename T>
NullStream& operator <<(NullStream& n,T const& data) {return n;}
NullStream& operator <<(NullStream& n,std::ostream& (*)(std::ostream&)) {return n;}
#ifdef _TEST_
#define myerr std::cerr
#else
NullStream myerrstream;
#define myerr myerrstream
#endif
int main()
{
myerr << "Hi" << std::endl;;
myerr << std::endl;;
}