LoadLibrary : crash in release mode [closed] - c++

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
I use the function LoadLibrary from the Windows API to load dynamically a DLL (compiled in release mode). Then I call an imported function my_function
When my exe is compiled in:
DEBUG mode: no issue
RELEASE mode w/ debug info: no issue
RELEASE mode w/out debug info: it crashes after LoadLibrary call, when I call the imported function my_function
Here's a code sample:
MyClass.cpp:
#include "myclass.h"
typedef int (__stdcall *f_my_function)(char*, int*);
MyClass::MyClass()
{
mDllHandler = NULL;
}
bool MyClass::loadLibrary()
{
qCritical() << "Loading library";
mDllHandler = LoadLibrary(L"my.dll");
qCritical() << "Library loaded";
if (!mDllHandler) {
qCritical() << "Error : could not load my.dll";
return false;
}
return true;
}
bool MyClass::freeLibrary()
{
if(!mDllHandler) {
qCritical() << "Error : DLL handler is null";
return false;
}
if(!FreeLibrary(mDllHandler)) {
qCritical() << "Error : could not unload my.dll";
return false;
}
mDllHandler = NULL;
return true;
}
bool MyClass::myFunction(const& QString str)
{
if(!mDllHandler) {
qCritical() << "Error : DLL handler is null";
return false;
}
f_my_function my_function = (f_my_function)GetProcAddress(mDllHandler, "my_function");
if (!my_function) {
qCritical() << "Error : Could not resolve the function my_function";
return false;
}
int size = str.size();
char* string = str.toLatin1().data();
int error = my_function(string, &size);
qDebug() << "my_function : Error code is " << error;
return !error;
}
MyClass.h:
#ifndef MYCLASS_H
#define MYCLASS_H
#include <QString>
class MyClass
{
public:
MyClass();
bool loadLibrary();
bool freeLibrary();
bool myFunction(const QString& str = "");
private:
HINSTANCE mDllHandler;
};
#endif // MYCLASS_H
main.cpp
#include <QCoreApplication>
#include <QDebug>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
MyClass myClass;
myClass.loadLibrary();
myClass.myFunction();
myClass.freeLibrary();
return a.exec();
}
I really have no idea why it crashes here.
EDIT : Here I don't have access to the source code of my.dll.

Well the crash (which eventually did not occur always at LoadLibrary call, but sometimes later in the code) was due to a wrong declaration of the imported functions.
In MyClass.cpp :
Wrong declaration:
typedef int (__stdcall *f_my_function)(char*, int*);
Correct declaration:
typedef int (*f_my_function)(char*, int*);
Indeed I wrongly declared f_my_function with __stdcall

Related

How do I decouple C++ project header dependencies?

"Platform" is a C++ DLL project that includes SDL2. The project is configured with SDL2 includes and library directories. "Sample" is a console application that includes and links to "Platform", but makes no reference to SDL2 whatsoever; other than that implicitly made by my including a thing that includes that library. "Sample" fails to compile on a cpp file in "Platform" with the error that it cannot find SDL2's header files.
I can add the SDL2 include directory to the "Sample" project, but this creates a coupling that seems to defeat the reason I have separated "Platform" from "Sample" in the first place. I looked into precompiled headers, but that seems to solve a different problem; and I couldn't figure out how to get "Sample" to include the "Platform" PCH anyway.
error:
Severity Code Description Project File Line Suppression State
Error C1083 Cannot open include file: 'SDL.h': No such file or directory Sample C:\Users\danie\Source\SingleScreen\Platform\display.h 6
sample.cpp:
#include "platform.h"
int main(int argc, char** argv)
{
Display_Configuration* display_configuration = new Display_Configuration();
display_configuration->window_title = "Sandbox";
display_configuration->dimension_x = 1280;
display_configuration->dimension_y = 720;
display_configuration->color_depth = 24;
display_configuration->is_fullscreen = false;
Platform* platform = new Platform(display_configuration);
return 0;
}
platform.h:
#ifndef SINGLESCREEN_PLATFORM_PLATFORM_H
#define SINGLESCREEN_PLATFORM_PLATFORM_H
#ifdef PLATFORM_EXPORTS
#define PLATFORM_API __declspec(dllexport)
#else
#define PLATFORM_API __declspec(dllimport)
#endif
#include "display.h"
#include "controller.h"
#include "synthesizer.h"
#include "sampler.h"
extern PLATFORM_API class Platform
{
private:
Display* _display;
Controller* _controller;
Synthesizer* _synthesizer;
Sampler* _sampler;
public:
Platform(Display_Configuration* display_configuration);
~Platform();
};
#endif //SINGLESCREEN_PLATFORM_PLATFORM_H
platform.cpp:
#include "pch.h"
#include "platform.h"
Platform::Platform(Display_Configuration* display_configuration)
{
_display = new Display(display_configuration);
_controller = new Controller();
_synthesizer = new Synthesizer();
_sampler = new Sampler();
}
Platform::~Platform()
{
delete _display;
delete _controller;
delete _synthesizer;
delete _sampler;
}
display.h:
#ifndef SINGLESCREEN_PLATFORM_DISPLAY_H
#define SINGLESCREEN_PLATFORM_DISPLAY_H
#include <vector>
#include <string>
#include "SDL.h"
struct Display_Configuration
{
std::string window_title;
bool is_fullscreen;
int dimension_x;
int dimension_y;
int color_depth;
};
class Display
{
private:
std::string _window_title;
bool _is_fullscreen;
int _dimension_x;
int _dimension_y;
int _color_depth;
SDL_Window* _window;
SDL_Surface* _surface;
public:
Display(Display_Configuration* display_configuration);
~Display();
bool initialize();
};
#endif //SINGLESCREEN_PLATFORM_DISPLAY_H
display.cpp:
#include "pch.h"
#include <iostream>
#include "display.h"
Display::Display(Display_Configuration* display_configuration)
{
_window_title = display_configuration->window_title;
_is_fullscreen = display_configuration->is_fullscreen;
_dimension_x = display_configuration->dimension_x;
_dimension_y = display_configuration->dimension_y;
_color_depth = display_configuration->color_depth;
}
Display::~Display()
{
}
bool Display::initialize()
{
_window = NULL;
_surface = NULL;
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
std::cout << "Failed to initialize the video subsystem. The error was:" << std::endl << SDL_GetError() << std::endl;
return false;
}
_window = SDL_CreateWindow(
_window_title.c_str(),
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
_dimension_x, _dimension_y,
SDL_WINDOW_SHOWN | ( _is_fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : NULL )
);
if (_window == NULL) {
std::cout << "Failed to create a rendering window. The error was:" << std::endl << SDL_GetError() << std:: endl;
return false;
}
_surface = SDL_GetWindowSurface(_window);
if (_surface == NULL) {
std::cout << "Failed to create a rendering surface. The error was:" << std::endl << SDL_GetError() << std::endl;
return false;
}
return true;
}
TL;DR:
move the include from the header into the source
forward declare SDL_Window and SDL_Surface
Judging by your profile, you recently decided to learn programming. Don't worry, that's cool and you'll figure it out.
Why do I think so? It took me a moment to understand your question because [Y]ou seem at a loss for the right words.
What you are asking for is how to hide implementation details properly in C++. As far as I understood, you're working on a library using SDL under the hood. And you wish to use this library in applications, that do not use SDL directly.
Don't worry, you can achieve that. You just need to fix two things:
Remove the line #include "SDL.h" from any public header and put the line into your cpp-files, that require it. Headers are considered public, if they're meant to be used by another target, like your sample.
Forward declare SDL's types.
Forward wat?
Compilers are of such nature, that they want to know everything. If they don't get that, you'll get slapped with compilation errors.
When it comes to types such as SDL_Window, the compiler wants to know:
Does it exist?
How big is the type?
What attributes does it have?
Which methods does it provide?
Luckily, we can tell the nosy compiler to mind its own business by using so called "forward declarations". I forward declaration looks like this:
// Example for your Display class.
class Display;
// Example for SDL
struct SDL_Window;
struct SDL_Surface;
Using forward declarations we make a promise, that the types Display, SDL_Window and SDL_Surface exist, without including their headers, and they'll be defined somewhere else.
This allows us to store pointers (or references) to instances of those types. This way the include #include "SDL.h" can be moved from the header display.h to the source display.cpp. Your sample wouldn't need to know about SDL's whereabouts.
IMPORTANT! Types that were forward declared cannot be used without definition.
Let's look say you forward declared your own class display in a file; i.e. like this:
class display;
int main() {
auto size = sizeof(display); // Nope!
auto ptr = new display{}; // Nope!
ptr->initialize(); // Nope!
display object_from_somewhere_else;
display* ptr2 = &object_from_somewhere_else; // Yes!
return 0;
};
In order to make a type useable again, we need to include the header that defines the type.
class display;
#include "display.h"
or
#include "display.h"
class display;
int main() {
auto size = sizeof(display); // Yes!
auto ptr = new display{}; // Yes!
ptr->initialize(); // Yes!
display object_from_somewhere_else;
display* ptr2 = &object_from_somewhere_else; // Yes!
return 0;
};
I know this might be a lot to for once. Just bear with me one more moment and let's take a look at how the final result would look like:
display.h
#ifndef SINGLESCREEN_PLATFORM_DISPLAY_H
#define SINGLESCREEN_PLATFORM_DISPLAY_H
#include <vector>
#include <string>
struct SDL_Window;
struct SDL_Surface;
struct Display_Configuration
{
std::string window_title;
bool is_fullscreen;
int dimension_x;
int dimension_y;
int color_depth;
};
class Display
{
private:
std::string _window_title;
bool _is_fullscreen;
int _dimension_x;
int _dimension_y;
int _color_depth;
SDL_Window* _window;
SDL_Surface* _surface;
public:
Display(Display_Configuration* display_configuration);
~Display();
bool initialize();
};
#endif //SINGLESCREEN_PLATFORM_DISPLAY_H
display.cpp
#include "pch.h"
#include <iostream>
#include "display.h"
#include "SDL.h"
Display::Display(Display_Configuration* display_configuration)
{
_window_title = display_configuration->window_title;
_is_fullscreen = display_configuration->is_fullscreen;
_dimension_x = display_configuration->dimension_x;
_dimension_y = display_configuration->dimension_y;
_color_depth = display_configuration->color_depth;
}
Display::~Display()
{
}
bool Display::initialize()
{
_window = NULL;
_surface = NULL;
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
std::cout << "Failed to initialize the video subsystem. The error was:" << std::endl << SDL_GetError() << std::endl;
return false;
}
_window = SDL_CreateWindow(
_window_title.c_str(),
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
_dimension_x, _dimension_y,
SDL_WINDOW_SHOWN | ( _is_fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : NULL )
);
if (_window == NULL) {
std::cout << "Failed to create a rendering window. The error was:" << std::endl << SDL_GetError() << std:: endl;
return false;
}
_surface = SDL_GetWindowSurface(_window);
if (_surface == NULL) {
std::cout << "Failed to create a rendering surface. The error was:" << std::endl << SDL_GetError() << std::endl;
return false;
}
return true;
}

LLVM error accessing loopinfo in function pass

I'm trying to get loop information from IR by writing a function pass. So I followed some examples and wrote like following. I'm not very familiar with writing passes and pass managers.
#include <iostream>
#include "llvm/LLVMContext.h"
#include "llvm/Module.h"
#include "llvm/Function.h"
#include "llvm/BasicBlock.h"
#include "llvm/Analysis/LoopInfo.h"
#include "llvm/Support/IRReader.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Analysis/LoopInfo.h"
#include "llvm/Analysis/LoopPass.h"
#include "llvm/Pass.h"
#include "llvm/PassManager.h"
using namespace llvm;
namespace {
class DetectLoop: public FunctionPass {
public:
DetectLoop() : FunctionPass(ID) {}
static char ID;
virtual void getAnalysisUsage(AnalysisUsage &AU) const {
AU.addRequired<LoopInfo>();//I'm not sure if it's correct here *****1*****
}
virtual bool runOnFunction(Function &F) {
if (!F.isDeclaration())
LoopInfo &li = getAnalysis<LoopInfo>(F);//*****2*****
for (Function::iterator I = F.begin(); I != F.end(); I++) {
BasicBlock *BB = I;
for (BasicBlock::iterator BI = BB->begin(), BE = BB->end(); BI != BE;) {
Instruction &I = *BI++;
//did noting inside
}
}
return false;
}
};
}
char DetectLoop::ID = 0;
int main(int argc, char** argv)
{
if (argc < 2) {
errs() << "Expected an argument - IR file name\n";
exit(1);
}
SMDiagnostic Err;
std::cout<<argv[1]<<std::endl;
Module *Mod = ParseIRFile(argv[1], Err, getGlobalContext());
Err.Print(argv[0], errs());
if (Mod) {
PassManager PM;
PM.add(new DetectLoop());
PM.add(new LoopInfo());//*****3*****
PM.run(*Mod);
}
else {
std::cout << "Mod is null" << std::endl;
}
return 0;
}
While I was running this program, it just showed me segmentation error(core dumped),
but when I commented out addRequired the error msg I got was
IRparser: PassManager.cpp:1200: virtual llvm::Pass* llvm::PMDataManager::getOnTheFlyPass
(llvm::Pass*, llvm::AnalysisID, llvm::Function&): Assertion `0 && "Unable to find on the fly pass"' failed.
Stack dump:
0. Running pass 'Function Pass Manager' on module '../../testcase/forloop1.ll'.
1. Running pass 'Unnamed pass: implement Pass::getPassName()' on function '#main'
Aborted (core dumped)
I have marked 3 places I'm not sure which is correct or not. Can anyone help me with that?
If you use it in a Module:
LoopInfo &li = getAnalysis<LoopInfo>(F)
If you use it in a function:
LoopInfo &li = getAnalysis<LoopInfo>()
I had this question before. After searching several answers, I found the solution.
You should change the location of the two statements below:
PM.add(new DetectLoop());
PM.add(new LoopInfo());//*****3*****
Because LoopInfo Pass must be registed before your own pass.

Access Violation Writing Location with SDL_Net

I've been struggling with this for half a day and it hasn't improved, so here it's:
I have a class called Network Handler that will handle all network connections in a game I'm making in SDL, I'm using SDL_Net for all the networking. Here is my header class:
#ifndef NETWORK_HANDLER
#define NETWORK_HANDLER
#include <iostream>
#include <string>
#include <SDL_net.h>
using namespace std;
class NetworkHandler
{
public:
NetworkHandler();
~NetworkHandler(){}
bool Initialize(bool pIsServer);
void CheckForMessages();
void exit();
private:
UDPpacket* m_packet;
UDPsocket m_socket;
};
#endif
And here is my Class code:
#include "NetworkHandler.h"
NetworkHandler::NetworkHandler():m_packet(new UDPpacket()),m_socket(0)
{
}
bool NetworkHandler::Initialize(bool pIsServer)
{
//Initialize SDL_net
if(SDLNet_Init() < 0)
{
std::cerr<< "SDL Net Error:"<< SDLNet_GetError() << std::endl;
return false;
}
atexit(SDLNet_Quit);
std::cout<< "SDL Net Error:"<< SDLNet_GetError() << std::endl;
m_socket = SDLNet_UDP_Open(2000);
if(!m_socket)
{
std::cerr<< "Socket Error:" <<SDLNet_GetError()<<std::endl;
return false;
}
if(!(m_packet = SDLNet_AllocPacket(512)))
{
std::cerr<<"Packet Error:"<<SDLNet_GetError()<<std::endl;
}
if(!pIsServer)
{
IPaddress ip_server;
if(SDLNet_ResolveHost(&ip_server,"10.167.193.140",2000)<0)
{
std::cerr<<"Failed to connect to Server: " << SDLNet_GetError() <<endl;
return false;
}
}
return true;
}
void NetworkHandler::CheckForMessages()
{
if(SDLNet_UDP_Recv(m_socket,m_packet))
{
std::cout<<m_packet->data<<std::endl;
}
}
void NetworkHandler::exit()
{
SDLNet_FreePacket(m_packet);
SDLNet_Quit();
}
I'm getting the following error:
Unhandled exception at 0x00a3e524 in NetworkGameDevelopmenProject.exe: 0xC0000005: Access violation writing location 0xcdcdcdd1.
At this line:
m_socket = SDLNet_UDP_Open(2000);
And when I take a look at m_socket during debug, it has the following error attached to it:
m_socket CXX0076: Error:
I've already tried cleaning and rebuilding. Funnily enough if I declare m_socket and m_packet locally(inside the function) it doesn't give me any errors. Can anyone think why this is happening?

MySQL C++ Connector crashes my app at ResultSet->getString()

It's me again probably asking noob C++ questions
I had MAJOR headaches making the darn (sorry for the language) MySQL C++ connector work. I don't know if it is poorly written or something, but for my experience yet I've never had so much trouble making something to work.
Anyhow I got it to connect and throw exceptions on failed connect/query which for me is quite big thing :U :P . The actual problem comes out of me obtaining the result of the query. Regardless of what I do my application always crashes :S
I used the 32-bit installer and the libmysql.dll/lib from the 32-bit MySQL server (since i'm compiling a 32-bit application i figured this is the right thing to do)
Here's some code so you could imagine what I'm talking about
DBManager.h
#ifndef DBMANAGER_H
#define DBMANAGER_H
#define CPPCONN_PUBLIC_FUNC
#define CPPCONN_LIB_BUILD True
#include <string>
#include "mysql_connection.h"
#include "mysql_driver.h"
#include <cppconn/driver.h>
#include <cppconn/exception.h>
#include <cppconn/resultset.h>
#include <cppconn/statement.h>
#include <cppconn/prepared_statement.h>
class DBManager
{
public:
static DBManager* Instance();
bool Query(const char* Query);
void Connect(const char* DbHost, unsigned short DbPort, const char* DbUser, const char* DbPass, const char* DbName);
bool ValidCredentials(const char* Username, const char* Password);
void ManageException(sql::SQLException &e);
~DBManager();
protected:
static DBManager* pInstance;
private:
DBManager() {};
DBManager(DBManager const&){};
DBManager& operator=(DBManager const&){};
sql::mysql::MySQL_Driver* driver;
sql::Connection *Con;
sql::PreparedStatement *pstmt;
sql::ResultSet *res;
sql::Statement *stmt;
bool isConnected;
};
#endif
And now the cpp file DBManager.cpp
#include "DBManager.h"
DBManager* DBManager::pInstance = NULL;
DBManager* DBManager::Instance()
{
if (!pInstance)
{
pInstance = new DBManager();
}
return pInstance;
}
bool DBManager::Query(const char* Query)
{
return true;
}
DBManager::~DBManager()
{
delete Con;
delete pstmt;
delete res;
delete stmt;
}
void DBManager::ManageException(sql::SQLException& e)
{
if (e.getErrorCode() != 0) {
std::cout << "# ERR: SQLException in " << __FILE__;
std::cout << "(" << __FUNCTION__ << ") on line " << __LINE__ << std::endl;
std::cout << "# ERR: " << e.what();
std::cout << " (MySQL error code: " << e.getErrorCode();
std::cout << ", SQLState: " << e.getSQLState() << " )" << std::endl;
}
}
void DBManager::Connect(const char* DbHost, unsigned short DbPort, const char* DbUser, const char* DbPass, const char* DbName)
{
try {
driver = sql::mysql::get_mysql_driver_instance();
std::string connDSN = "tcp://" + std::string(DbHost) + ":3306";
Con = driver->connect(connDSN, sql::SQLString(DbUser), sql::SQLString(DbPass));
Con->setSchema(sql::SQLString(DbName));
isConnected = true;
std::cout<<"Database connection successul."<<std::endl;
} catch(sql::SQLException &e) {
ManageException(e);
isConnected = false;
return;
}
}
bool DBManager::ValidCredentials(const char* Username, const char* Password)
{
bool cred = false;
try {
pstmt = Con->prepareStatement("SELECT * FROM account WHERE account_name=? LIMIT 1"); // Smart use of indexing
pstmt->setString(1, Username);
res = pstmt->executeQuery();
while(res->next())
{
if (res->getString("password") == Password)
{
cred = true;
}
}
}
catch(sql::SQLException &e) {
ManageException(e);
return false;
}
return cred;
}
Basically, It compiles without a problem, Connects without a problem, Executes queries without a problem, but the second I try to retrieve data some breakpoint exception is thrown in a file "xutils.cpp". I really have no idea what I'm doing wrong. I'm using the DEBUG libraries while compiling for debug. Hmm libmysql.dll should be release since I extracted it from the server bundle, but I don't seem to find it as a source to compile my own.
I really have no idea why it crashes and burn like that :/
PS: Don't mind the no hashing of the password, it really is just a proof of concept to me in the way of ... getting it to work first, then securing it :U
PS: I also have Boost libraries compiled and ready in the project, if that would help :U
EDIT: Main function
bool ServerRunning = true;
int main(int argc, char** argv)
{
#ifdef _WIN32
std::string title = TEXT("Window Title Change");
SetConsoleTitle(title.c_str());
#endif;
std::cout<<"Loading Configuration File..."<<std::endl<<std::endl;
std::string path = boost::filesystem::path(boost::filesystem::current_path()).string();
path += "\\Config.ini";
INIParser* Config = new INIParser(path.c_str()); //MinINI
// Sockets data
std::string listenIP = Config->GetString("Network", "ListenIP", "127.0.0.1");
unsigned short listenPort = Config->GetInt("Network", "ListenPort", 5000);
// Database data
std::string dbHost = Config->GetString("Database", "Host", "localhost");
std::string dbUser = Config->GetString("Database", "User", "root");
std::string dbPass = Config->GetString("Database", "Password", "");
std::string dbName = Config->GetString("Database", "Database", "authserv");
unsigned short dbPort = Config->GetInt("Database", "Post", 1000);
// General settings
int sessionTimeout = Config->GetInt("Settings", "SessionTimeout", 10);
int maxClients = Config->GetInt("Settings", "MaxClients", 10);
int serverTimeout = Config->GetInt("Settings", "GameserverTimeout", 1);
// Begin Initialization
DBManager::Instance()->Connect(dbHost.c_str(), dbPort, dbUser.c_str(), dbPass.c_str(), dbName.c_str());
bool loginSuccess = DBManager::Instance()->ValidCredentials("Username", "Password");
char c;
while (ServerRunning)
{
std::cin>>c;
if (c == 'q')
{
ServerRunning = false;
}
}
return 0;
}
Assuming the password field is defined as varchar in the database, you cannot use getString() to retrieve it. You must instead use the blob function, getBlob().
This is how the while loop would look:
while(res->next())
{
std::istream * retrievedPassword_stream = res->getBlob("password");
if (retrievedPassword_stream)
{
char pws[PASSWORD_LENGTH+1]; // PASSWORD_LENGTH defined elsewhere; or use other functions to retrieve it
retrievedPassword_stream->getline(pws, PASSWORD_LENGTH);
std::string retrievedPassword(pws); // also, should handle case where Password length > PASSWORD_LENGTH
if (retrievedPassword == std::string(Password))
{
cred = true;
}
}
}
Side comments: Note that there are some other issues with the code.
The statement handle must be deleted, so you should do a delete pstmt; at the appropriate place in the ValidCredentials() function (rather than in the destructor). (But, why use a prepared statement in that case anyways? Better to initialize the prepared statement in the constructor (or somewhere else outside the function the query is called), as well as delete in the destructor or elsewhere, if you do use a prepared statement. Instead of a prepared statement, though, note that prepared statements are most useful for very high-use and high-CPU intensive queries, so using it for password validation might not be important here (you could just execute a regular query, instead of a prepared statement).)
Likewise, the ResultSet needs to be deleted (delete res) at the end of the try block, rather than in the destructor.
Be sure to check for NULL before using pstmt, res, or Con.
stmt appears to be unused and should not be deleted.
download mysql c++ connector
compile mysqlcppconn-static project use mt or mtd
your project add CPPCONN_LIB_BUILD
your project add (2) built static library

Small logger class [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 7 months ago.
The community reviewed whether to reopen this question 7 months ago and left it closed:
Original close reason(s) were not resolved
Improve this question
I am looking for a small lightweight logging system in c++. I have found some existing frameworks but I don't need all of their features at this point in time. I primarily am looking for a small system that can for example configure the log level output file. I am looking for an existing solution as I don't want to reinvent the wheel.
I strongly recommend this simple logging system: http://www.drdobbs.com/cpp/201804215. It is composed of a single header file. I have successfully used it on Linux, Windows and Mac OS X.
You write to the log like this:
FILE_LOG(logWARNING) << "Ops, variable x should be " << expectedX << "; is " << realX;
I really like the stream syntax. It is unobtrusive, typesafe and expressive. The logging framework automatically adds a \n at the end of the line, plus date, time and indentation.
Configuring the logs is pretty easy:
FILELog::ReportingLevel() = logDEBUG3;
FILE* log_fd = fopen( "mylogfile.txt", "w" );
Output2FILE::Stream() = log_fd;
This framework is also easy to extend. At work, we have recently made some adaptations to it so that it now uses an std::ofstream instead of a FILE*. As a result, we are now able to add nice features such as encrypting the logs, by chaining the streams.
For anyone wanting a simple solution, I recommend: easylogging++
Single header only C++ logging library. It is extremely light-weight,
robust, fast performing, thread and type safe and consists of many
built-in features. It provides ability to write logs in your own
customized format. It also provide support for logging your classes,
third-party libraries, STL and third-party containers etc.
This library has everything built-in to prevent usage of external
libraries.
Simple example: (more advanced examples available on the link above).
#include "easylogging++.h"
INITIALIZE_EASYLOGGINGPP
int main(int argv, char* argc[]) {
LOG(INFO) << "My first info log using default logger";
return 0;
}
Example output inside a class:
2015-08-28 10:38:45,900 DEBUG [default] [user#localhost]
[Config::Config(const string)] [src/Config.cpp:7] Reading config file:
'config.json'
I tried log4cpp and boost::log but they are not as easy as this one.
EXTRA CONTENT: Minimal version - LOG header
I created a small code for even simpler applications based on easylogging but requires no initialization (be aware that it is probably not thread safe). Here is the code:
/*
* File: Log.h
* Author: Alberto Lepe <dev#alepe.com>
*
* Created on December 1, 2015, 6:00 PM
*/
#ifndef LOG_H
#define LOG_H
#include <iostream>
using namespace std;
enum typelog {
DEBUG,
INFO,
WARN,
ERROR
};
struct structlog {
bool headers = false;
typelog level = WARN;
};
extern structlog LOGCFG;
class LOG {
public:
LOG() {}
LOG(typelog type) {
msglevel = type;
if(LOGCFG.headers) {
operator << ("["+getLabel(type)+"]");
}
}
~LOG() {
if(opened) {
cout << endl;
}
opened = false;
}
template<class T>
LOG &operator<<(const T &msg) {
if(msglevel >= LOGCFG.level) {
cout << msg;
opened = true;
}
return *this;
}
private:
bool opened = false;
typelog msglevel = DEBUG;
inline string getLabel(typelog type) {
string label;
switch(type) {
case DEBUG: label = "DEBUG"; break;
case INFO: label = "INFO "; break;
case WARN: label = "WARN "; break;
case ERROR: label = "ERROR"; break;
}
return label;
}
};
#endif /* LOG_H */
Usage:
#include "Log.h"
int main(int argc, char** argv) {
//Config: -----(optional)----
structlog LOGCFG = {};
LOGCFG.headers = false;
LOGCFG.level = DEBUG;
//---------------------------
LOG(INFO) << "Main executed with " << (argc - 1) << " arguments";
}
This code print the message using "cout", but you can change it to use "cerr" or append a file, etc. I hope its useful to someone. (Note: I'm not C++ expert in any way, so this code may explode in extreme cases).
I recommend to try plog library (I'm the author). It's about 1000 lines of code, header only and easy to use:
#include <plog/Log.h>
int main()
{
plog::init(plog::debug, "Sample.log");
LOGD << "Hello log!";
LOGD_IF(true) << "conditional logging";
return 0;
}
all of the mentioned loggers so far make use of macros for logging calls. To me, that is so ugly, I don't care about what performance boost that gives, I won't go near it.
https://github.com/gabime/spdlog is what I like. Clean syntax, handles all typical usages. Fast and small. e.g. for a file logger it is:
auto my_logger = spd::basic_logger_mt("basic_logger", "logs/basic.txt");
my_logger->info("Some log message");
This question has my attempt with some fanciness. It is completely Standard C++ and makes no platform assumptions whatsoever. It basically consists of a temporary object used like this:
Debug(5) << "This is level 5 debug info.\n";
I'm sure you can figure out how to specify different files and other stuff when you have the basic layout. I tried to keep the class structured so that in a release build, every form of Debug output is removed as good as possible.
Mind you: if you specify a filename each time you construct it, and open the file and close it again, performance will suffer. In the case of multiple output files, it would certainly be best to have several static data members that open the different files when the program is run or if they are opened for the first time.
If you don't have size limitations on the project and you expect it to live a long time, I would suggest looking at Apache Log4cxx. It's not a small library, but it supports just about everything you ever wanted (including some things you didn't even knew you wanted) in logging, and it's portable.
In any larger project sooner or later you'll want your logging solution to do more than a "small logger class", so indeed why reinvent the wheel.
An update to Dr. Dobb's "A Lightweight Logger for C++":
There are actually a couple of loggers referred to in Dr. Dobb's. The first one Logging In C++ which is listed in one of the answers. I tried to use this one but the source is no longer available on the Dr. Dobb's site.
The second one that worked for me and that I recommend is A Lightweight Logger for C++ by Filip Janiszewski working at Nokia Siemens Networks. At first I had some problems getting this code to run so as I was searching for solutions, I ran across an update by the original author at: GitHub: fjanisze/logger. I found this code to be easy to understand, modify, and to use. It is thread safe and works with Visual Studio with Windows.
Another logger mentioned above is easylogging++ . When I first tried this one it looked promising. But when I added threading and sockets2 under Windows, it crashed. I did have the defines set for threading and Sock2 but I still couldn't get it to work so I can't recommend this one. The source code is also very complex so I had no chance to modify and fix it within a reasonable amount of time.
The above answers are all great.
Doubt anyone will ever see this answer, but this is what I use
https://github.com/asn10038/Cpp_Logger
Easy to set up after a configuration of 4-5 variable names in the .h file, and implemented without non standard dependencies.
Not header only but could be pretty easily.
Maybe this helps someone.
I, as well as many others, also answered this question with some code.
This isn't really "ready" in all ways, but it could be easily modified:
#pragma once
#include <codecvt>
#include <condition_variable>
#include <fstream>
#include <iostream>
#include <locale>
#include <memory>
#include <mutex>
#include <ostream>
#include <queue>
#include <sstream>
#include <string>
#include <thread>
#include <unordered_map>
#include <vector>
#ifdef _WIN32
#include <windows.h>
#endif
#include <string.h>
#define LOGL(level, msg) \
if (Loggy::isLevel(level)) { \
Loggy::writer(level, __FILE__, __LINE__) << msg; \
Loggy::queue(); \
}
#define LOG_FLUSH() \
{ \
Loggy::wait_queues(); \
}
#define LOGT(msg) LOGL(Loggy::LTRACE, msg)
#define LOGD(msg) LOGL(Loggy::LDEBUG, msg)
#define LOGI(msg) LOGL(Loggy::LINFO, msg)
#define LOGE(msg) LOGL(Loggy::LERROR, msg)
namespace Loggy {
using namespace std;
constexpr int DEFAULT_BUF_CNT = 1000;
constexpr const char *DEFAULT_TIME_FMT = "%Y%m%d.%H%M%S";
constexpr double DROP_NOTIFY_SECONDS = 5.0;
constexpr double FLUSH_SECONDS = 1.0;
enum {
LINVALID = 0,
LTRACE = 9,
LDEBUG = 10,
LINFO = 20,
LERROR = 40,
LWARN = 30,
LCRITICAL = 50,
LMAX = 50,
};
unordered_map<int, string> levelNames_ = {
{ LINVALID, "INVALID" },
{ LTRACE, "TRACE" },
{ LDEBUG, "DEBUG" },
{ LINFO, "INFO" },
{ LERROR, "ERROR" },
{ LWARN, "WARN" },
{ LCRITICAL, "CRITICAL" },
};
wstring str2w(const string &in)
{
#ifdef _WIN32
if (in.empty())
return std::wstring();
int size_needed = MultiByteToWideChar(CP_UTF8, 0, &in[0], (int)in.size(), NULL, 0);
std::wstring wstrTo(size_needed, 0);
MultiByteToWideChar(CP_UTF8, 0, &in[0], (int)in.size(), &wstrTo[0], size_needed);
return wstrTo;
#else
thread_local std::wstring_convert<std::codecvt_utf8<wchar_t>> wcu16;
return wcu16.from_bytes(in);
#endif
}
string w2str(const wstring &in)
{
#ifdef _WIN32
if (in.empty())
return std::string();
int size_needed
= WideCharToMultiByte(CP_UTF8, 0, &in[0], (int)in.size(), NULL, 0, NULL, NULL);
std::string strTo(size_needed, 0);
WideCharToMultiByte(CP_UTF8, 0, &in[0], (int)in.size(), &strTo[0], size_needed, NULL, NULL);
return strTo;
#else
thread_local std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> wcu8;
return wcu8.to_bytes( in );
#endif
}
template <class T> class SafeQueue {
public:
SafeQueue(void)
: q()
, m()
, c()
, x()
{
}
~SafeQueue(void) { lock_guard<mutex> lock(m); }
// Add an element to the queue.
void push(T t)
{
lock_guard<mutex> lock(m);
q.push(t);
c.notify_one();
}
// Get the "front"-element.
// If the queue is empty, wait till a element is avaiable.
T pop(void)
{
unique_lock<mutex> lock(m);
while (!x && q.empty()) {
// release lock as long as the wait and reaquire it afterwards.
c.wait(lock);
}
if (x) {
return T();
};
T val = q.front();
q.pop();
if (q.empty()) {
c.notify_all();
}
return val;
}
size_t size() { return q.size(); }
void join(void)
{
unique_lock<mutex> lock(m);
while (!q.empty()) {
c.wait(lock);
}
}
size_t drain(void)
{
unique_lock<mutex> lock(m);
std::queue<T> empty;
swap(q, empty);
c.notify_all();
return empty.size();
}
size_t quit()
{
x = true;
return drain();
}
private:
queue<T> q;
mutable mutex m;
condition_variable c;
bool x;
};
static string timestamp(const char format[], const time_t &rawtime)
{
struct tm timeinfo;
char buffer[120];
#ifdef _WIN32
localtime_s(&timeinfo, &rawtime);
#else
localtime_r(&rawtime, &timeinfo);
#endif
strftime(buffer, sizeof(buffer), format, &timeinfo);
return string(buffer);
}
#ifdef _WIN32
#define _LOGGY_CVT_FILENAME(s) s
#else
#define _LOGGY_CVT_FILENAME(s) Loggy::w2str(s)
#endif
class Output {
SafeQueue<wstring> queue_; // this should be first
wofstream fstream_;
wostream &wstream_;
size_t max_;
int level_;
size_t dropped_ = 0;
bool alive_ = true;
time_t firstDrop_ = 0;
std::thread thread_; // this must be last
public:
Output(wostream &s, int level, int max)
: wstream_(s)
, level_(level)
, max_(max)
, thread_(&Output::worker, this)
{
}
Output(const wstring &s, int level, size_t max)
: fstream_(_LOGGY_CVT_FILENAME(s), std::wofstream::out | std::wofstream::app)
, wstream_(fstream_)
, level_(level)
, max_(max)
, thread_(&Output::worker, this)
{
}
~Output()
{
alive_ = false;
dropped_ += queue_.quit();
if (dropped_) {
logDropped();
}
thread_.join();
}
void wait() { queue_.join(); wstream_.flush(); }
void logDropped()
{
wstringstream ws;
time_t t;
time(&t);
ws << Loggy::timestamp(DEFAULT_TIME_FMT, t).c_str();
ws << " dropped " << dropped_ << " entries";
queue_.push(ws.str());
dropped_ = 0;
}
void add(wstring &str, time_t &t)
{
if (alive_) {
if (max_ == 0 || queue_.size() < max_) {
queue_.push(str);
} else {
++dropped_;
if (dropped_ == 1) {
firstDrop_ = t;
} else if (difftime(t, firstDrop_) > DROP_NOTIFY_SECONDS) {
logDropped();
}
}
}
}
void worker()
{
int written = 0;
time_t lastFlush = 0;
while (alive_) {
if (!queue_.size() && written > 0) {
time_t t;
time(&t);
if (difftime(t, lastFlush) > FLUSH_SECONDS) {
wstream_.flush();
lastFlush = t;
written = 0;
}
}
auto t = queue_.pop();
if (alive_) {
wstream_ << t << std::endl;
written += 1;
}
}
}
};
class Log {
public:
~Log() { resetOutput(); };
int level_ = LINFO;
int trigFrom_ = LINVALID;
int trigTo_ = LINVALID;
int trigCnt_ = LINVALID;
string timeFormat_ = DEFAULT_TIME_FMT;
mutex mutex_;
deque<Output> outputs_;
Output default_output_;
vector<wstring> buffer_;
Log()
: default_output_(wcout, LINFO, 1) {};
bool isLevel(int level) { return level >= level_; }
void resetOutput()
{
lock_guard<mutex> lock(mutex_);
outputs_.clear();
}
void addOutput(const wstring &path, int level, int bufferSize)
{
lock_guard<mutex> lock(mutex_);
outputs_.emplace_back(path, level, bufferSize);
}
void addOutput(wostream &stream, int level, int bufferSize)
{
lock_guard<mutex> lock(mutex_);
outputs_.emplace_back(stream, level, bufferSize);
}
std::vector<const char *> getFiles()
{
std::vector<const char *> ret;
return ret;
}
void setTrigger(int levelFrom, int levelTo, int lookbackCount)
{
trigFrom_ = levelFrom;
trigTo_ = levelTo;
trigCnt_ = lookbackCount;
}
void setLevel(int level) { level_ = level; }
struct LastLog {
wstringstream ws;
time_t tm = 0;
};
static LastLog &lastLog()
{
thread_local LastLog ll_;
return ll_;
}
static const char *basename(const char *file)
{
const char *b = strrchr(file, '\\');
if (!b)
b = strrchr(file, '/');
return b ? b + 1 : file;
}
static const char *levelname(int level) { return levelNames_[level].c_str(); }
wostream &writer(int level, const char *file, int line)
{
auto &ll = lastLog();
time(&ll.tm);
ll.ws.clear();
ll.ws.str(L"");
return ll.ws << timestamp(timeFormat_.c_str(), ll.tm).c_str() << " " << basename(file)
<< ":" << line << " " << levelname(level) << " ";
}
void queue()
{
lock_guard<mutex> lock(mutex_);
auto &ll = lastLog();
auto s = ll.ws.str();
if (outputs_.empty()) {
default_output_.add(s, ll.tm);
} else {
for (auto &out : outputs_) {
out.add(s, ll.tm);
}
}
}
void wait_queues()
{
if (outputs_.empty()) {
default_output_.wait();
} else {
for (auto &out : outputs_) {
out.wait();
}
}
}
};
static Log &getInstance()
{
static Log l;
return l;
}
void resetOutput() { getInstance().resetOutput(); }
void addOutput(const wstring &path, int level = LDEBUG, int bufferSize = DEFAULT_BUF_CNT)
{
getInstance().addOutput(path, level, bufferSize);
}
void addOutput(wostream &stream, int level = LDEBUG, int bufferSize = DEFAULT_BUF_CNT)
{
getInstance().addOutput(stream, level, bufferSize);
}
void setTrigger(int levelFrom, int levelTo, int lookbackCount)
{
getInstance().setTrigger(levelFrom, levelTo, lookbackCount);
}
std::vector<const char *> getFiles() { return getInstance().getFiles(); }
void setLevel(int level) { getInstance().setLevel(level); }
bool isLevel(int level) { return getInstance().isLevel(level); }
wostream &writer(int level, const char *file, int line)
{
return getInstance().writer(level, file, line);
}
void queue() { getInstance().queue(); }
void wait_queues() { getInstance().wait_queues(); }
} // end namespace Loggy
Features:
writing to the log doesn't block on i/o
similar macros to other solutions (LOGE(blah << stream))
prefers discarding log entries to slowing down
lazy flushing
header only, very small, stl classes only
tested on osx/win/nix
time format is configurable
Missing stuff:
easy, flexible log formatting (predefining a macro would be fine)
triggers have an interface but don't work yet
microseconds aren't working yet
If anyone actually likes this solution in any way, lmk and I'll make a real repo out of it with tests, etc. It's pretty fast. Probably not as fast as speedlogger (a heavier feature complete library), but not sure.
Original gist:
https://gist.github.com/earonesty/977b14c93358fe9b9ee674baac5d42d7
i created a small logging class, cuz i had issues to include other examples in VSCode while compiling, here for a one file header:
#include <iostream>
#include <fstream>
#include <string>
#include <iomanip>
#include <ctime>
#include <sstream>
using namespace std;
class logging
{
private:
ofstream myfile;
std::string get_time()
{
auto t = std::time(nullptr);
auto tm = *std::localtime(&t);
std::ostringstream oss;
//2047-03-11 20:18:26
oss << std::put_time(&tm, "%Y-%m-%d-%H:%M:%S");
auto str = oss.str();
return str;
}
public:
logging(string filepath)
{
myfile.open (filepath);
}
~logging()
{
myfile.close();
}
void write(string line)
{
myfile << get_time() << " " << line <<std::endl;
}
};