I wrote a c-code designed for linux platform.
Now, I want to make it cross-platform so to use in Windows as-well.
In my code, I dlopen an so file and utilize the functions inside it.
Below is how my code looks like. But I just found out that in windows, the way to load and use dynamic library is quite different.
void *mydynlib
mydynlib= dlopen("/libpath/dynlib.so",RTLD_LAZY);
void (*dynfunc1)() = dlsym(mydynlib,"dynfunc1");
void (*dynfunc2)(char*, char*, double) = dlsym(mydynlib,"dynfunc2");
int (*dynfunc3)() = dlsym(mydynlib,"dynfunc3");
From what I found, I need to use LoadLibrary & GetProcAddress instead of dlopen & dlsym. However, I do not know how to convert above line for windows using those. I've tried to search some examples for hours but couldn't find exact solution. If someone had this kind of experience, please give me a tip.
Excuse me if this is too obvious problem. I'm quite new to C. I usually write my program in python.
Once in my youth I created something like this:
/* dlfcn.h */
#ifndef DLFCN_H
#define DLFCN_H
#define RTLD_GLOBAL 0x100 /* do not hide entries in this module */
#define RTLD_LOCAL 0x000 /* hide entries in this module */
#define RTLD_LAZY 0x000 /* accept unresolved externs */
#define RTLD_NOW 0x001 /* abort if module has unresolved externs */
/*
How to call in Windows:
void *h = dlopen ("path\\library.dll", flags)
void (*fun)() = dlsym (h, "entry")
*/
#ifdef __cplusplus
extern "C" {
#endif
void *dlopen (const char *filename, int flag);
int dlclose (void *handle);
void *dlsym (void *handle, const char *name);
const char *dlerror (void);
#ifdef __cplusplus
}
#endif
#endif
and dlfcn.c:
/* dlfcn.c */
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
static struct {
long lasterror;
const char *err_rutin;
} var = {
0,
NULL
};
void *dlopen (const char *filename, int flags)
{
HINSTANCE hInst;
hInst= LoadLibrary (filename);
if (hInst==NULL) {
var.lasterror = GetLastError ();
var.err_rutin = "dlopen";
}
return hInst;
}
int dlclose (void *handle)
{
BOOL ok;
int rc= 0;
ok= FreeLibrary ((HINSTANCE)handle);
if (! ok) {
var.lasterror = GetLastError ();
var.err_rutin = "dlclose";
rc= -1;
}
return rc;
}
void *dlsym (void *handle, const char *name)
{
FARPROC fp;
fp= GetProcAddress ((HINSTANCE)handle, name);
if (!fp) {
var.lasterror = GetLastError ();
var.err_rutin = "dlsym";
}
return (void *)(intptr_t)fp;
}
const char *dlerror (void)
{
static char errstr [88];
if (var.lasterror) {
sprintf (errstr, "%s error #%ld", var.err_rutin, var.lasterror);
return errstr;
} else {
return NULL;
}
}
You could use a set of macros that change depending on the OS you're on:
#ifdef __linux__
#define LIBTYPE void*
#define OPENLIB(libname) dlopen((libname), RTLD_LAZY)
#define LIBFUNC(lib, fn) dlsym((lib), (fn))
#elif defined(WINVER)
#define LIBTYPE HINSTANCE
#define OPENLIB(libname) LoadLibraryW(L ## libname)
#define LIBFUNC(lib, fn) GetProcAddress((lib), (fn))
#endif
I'm trying to write a C++ program that will communicate with different DB providers. Everything works fine now, however upon exiting I'm trying to close the connection and the disconnect command crashes. At the moment I'm testing with SQLite, so it probably belong to this specific one.
Do I have to close the connection to the SQLite upon program termination? I believe generally it is a good idea to release the resources the program is using, but what about this case? I mean in this case the SQLite DB file will be released back to the system when the program exits, so its not actually a loss.
Thank you.
[EDIT]
As requested I'm submitting the code that I use.
In DLL1 (database.h):
#ifndef DBMANAGER_DATABASE
#define DBMANAGER_DATABASE
class __declspec(dllexport) Database
{
public:
Database();
virtual ~Database();
static void *operator new(size_t size);
static void operator delete(void *ptr, size_t size);
};
#endif
In DLL1 (database.cpp)
#include <string>
#include "database.h"
Database::Database()
{
}
Database::~Database()
{
}
void *Database::operator new(std::size_t size)
{
return ::operator new( size );
}
void Database::operator delete(void *ptr, std::size_t size)
{
return ::operator delete( ptr );
}
In DLL2: (database_sqlite.h):
#ifndef DBMANAGER_SQLITE
#define DBMANAGER_SQLITE
class __declspec(dllexport) SQLiteDatabase : public Database
{
public:
SQLiteDatabase();
virtual ~SQLiteDatabase();
virtual int Connect(const char *selectedDSN, std::vector<std::wstring> &errorMsg);
protected:
void GetErrorMessage(int code, std::wstring &errorMsg);
private:
sqlite3 *m_db;
};
#endif
In DLL2: (database_sqlite.cpp)
#ifdef WIN32
#include <windows.h>
#pragma execution_character_set("utf-8")
#endif
#include <vector>
#include <string>
#include <sqlext.h>
#include "sqlite3.h"
#include "database.h"
#include "database_sqlite.h"
SQLiteDatabase::SQLiteDatabase() : Database()
{
}
SQLiteDatabase::~SQLiteDatabase()
{
sqlite3_close( m_db );
}
int SQLiteDatabase::Connect(const char *selectedDSN, std::vector<std::wstring> &errorMsg)
{
int result = 0;
std::wstring errorMessage;
int res = sqlite3_open( selectedDSN, &m_db );
if( res != SQLITE_OK )
{
// get error messages GetErrorMessage( res, errorMessage );
errorMsg.push_back( errorMessage );
result = 1;
}
else
{
res = sqlite3_exec( m_db, "PRAGMA foreign_keys = ON", NULL, NULL, NULL );
if( res != SQLITE_OK )
{
// get error message GetErrorMessage( res, errorMessage );
errorMsg.push_back( errorMessage );
result = 1;
}
}
return result;
}
In DLL3: (dialogs.cpp)
#ifdef __GNUC__
#pragma implementation "dialogs.h"
#endif
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#include <vector>
#include "sqlite3ext.h"
#include "database.h"
#include "database_sqlite.h"
#ifdef _WINDOWS
BOOL APIENTRY DllMain( HANDLE hModule, DWORD fdwReason, LPVOID lpReserved)
{
lpReserved = lpReserved;
hModule = hModule;
int argc = 0;
char **argv = NULL;
switch( fdwReason )
{
case DLL_PROCESS_ATTACH:
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
#endif
extern "C" __declspec(dllexport) Database *DatabaseProfile(Database *db)
{
db = new SQLiteDatabase;
return db;
}
In main application (test_app.cpp):
#include "stdafx.h"
#include "database.h"
#include <windows.h>
#include <stdio.h>
typedef Database *(__cdecl *MYFUNC)(Database *);
class A
{
public:
A(Database *db)
{
m_db = NULL;
HINSTANCE hInst = LoadLibrary( TEXT( "dialogs.dll" ) );
MYFUNC func = (MYFUNC) GetProcAddress( hInst, "DatabaseProfile" );
m_db = func( db );
}
~A()
{
}
Database *GetDatabase()
{
return m_db;
}
private:
Database *m_db;
};
int _tmain(int argc, _TCHAR* argv[])
{
Database *db = NULL, *m_db;
A *a = new A( db );
m_db = a->GetDatabase();
delete m_db;
delete a;
return 0;
}
[EDIT]
Running this code I am getting the crash executing "delete "m_db" line. SQLite is compiled in from source code.
Any idea?
Thank you.
I have the classic problem as given here, here and here and also here,
However, I would like a child process to insert an element at the end of a doubly linked list. a point to the first element of the list is global, and I want to access all of the list elements form the main process, and also next time I branch the main using fork, I want to be abe to access all elements, and update them, and insert more elements, in turn the main process again being able to access the modifyied list.
Each process exits with a system call with execvp (i need them to be able to call stuff using varied number of arameters).
I perhaps am asking a too broad question, but I personally did not get any further than branching and the inserting an element at the end of the list. Thus I actually dont have a single line of code that takes me where I want to go. I have no idea how to use shm() in this scenario.
Please help.
You can try this.. I just wrote it from scratch.. It is cross-platform so that's always a plus. The allocators and pools can be re-used with anything. For example, you can make any stl container allocate on the stack or wherever you want..
SharedMemory.hpp:
#ifndef SHAREDMEMORY_HPP_INCLUDED
#define SHAREDMEMORY_HPP_INCLUDED
#if defined _WIN32 || defined _WIN64
#include <windows.h>
#else
#include <sys/types.h>
#include <sys/mman.h>
#include <dlfcn.h>
#include <fcntl.h>
#include <unistd.h>
#endif
#include <string>
#include <cstdint>
class SharedMemory
{
private:
std::string name;
std::size_t size;
void* data;
void* hFileMap;
public:
SharedMemory(std::string name, std::size_t size) : name(name), size(size), data(nullptr), hFileMap(nullptr) {};
~SharedMemory();
bool Open();
bool Create();
std::size_t GetSize() const {return this->size;}
void* GetPointer() const {return this->data;}
};
#endif // SHAREDMEMORY_HPP_INCLUDED
SharedMemory.cpp:
#include "SharedMemory.hpp"
SharedMemory::~SharedMemory()
{
if (data)
{
#if defined _WIN32 || defined _WIN64
UnmapViewOfFile(data);
data = nullptr;
if (hFileMap)
{
if (CloseHandle(hFileMap))
{
hFileMap = nullptr;
}
}
#else
if (data)
{
munmap(data, size);
data = nullptr;
}
if (hFileMap)
{
if (!close(hFileMap))
{
hFileMap = nullptr;
}
}
#endif
}
}
bool SharedMemory::Open()
{
#if defined _WIN32 || defined _WIN64
if ((hFileMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, false, name.c_str())) == nullptr)
{
return false;
}
if ((data = MapViewOfFile(hFileMap, FILE_MAP_ALL_ACCESS, 0, 0, size)) == nullptr)
{
CloseHandle(hFileMap);
return false;
}
#else
if ((hFileMap = open(MapName.c_str(), O_RDWR | O_CREAT, 438)) == -1)
{
return false;
}
if ((data = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, hFileMap, 0)) == MAP_FAILED)
{
close(hFileMap);
return false;
}
#endif
return true;
}
bool SharedMemory::Create()
{
#if defined _WIN32 || defined _WIN64
if ((hFileMap = CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, size, name.c_str())) == nullptr)
{
return false;
}
if ((data = MapViewOfFile(hFileMap, FILE_MAP_ALL_ACCESS, 0, 0, size)) == nullptr)
{
CloseHandle(hFileMap);
return false;
}
#else
if ((hFileMap = open(MapName.c_str(), O_RDWR | O_CREAT, 438)) == -1)
{
return false;
}
if ((data = mmap(nullptr, Size, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, hFileMap, 0)) == MAP_FAILED)
{
close(hFileMap);
return false;
}
#endif
return true;
}
Pools.hpp:
#ifndef POOLS_HPP_INCLUDED
#define POOLS_HPP_INCLUDED
#include <stdexcept>
#include <cstdint>
#include "SharedMemory.hpp"
template<typename T>
class SharedPool
{
private:
T* data;
SharedMemory* shm;
std::size_t size;
public:
SharedPool(SharedMemory* shm) : data(reinterpret_cast<T*>(shm->GetPointer())), shm(shm), size(shm->GetSize()) {};
template<typename U = T>
void* allocate(std::size_t n, const void* hint = 0) {return &data[0];}
template<typename U = T>
void deallocate(U* ptr, std::size_t n) {}
template<typename U = T>
std::size_t max_size() const {return size;}
};
#endif // POOLS_HPP_INCLUDED
main.cpp (adding values to shared memory from process one):
#include "SharedMemory.hpp"
#include "Allocators.hpp"
#include "Pools.hpp"
#include <vector>
#include <iostream>
int main()
{
SharedMemory mem = SharedMemory("Local\\Test_Shared_Memory", 1024);
if (!mem.Open() && !mem.Create())
{
throw std::runtime_error("Error Mapping Shared Memory!");
}
auto pool = PoolAllocator<int, SharedPool<int>>(SharedPool<int>(&mem));
std::vector<int, decltype(pool)> v(pool);
int* ptr = reinterpret_cast<int*>(mem.GetPointer());
std::cout<<"Pushing 3 values to: "<<ptr<<"\n";
v.push_back(100);
v.push_back(200);
v.push_back(700);
std::cin.get();
}
main.cpp (Reading values from shared memory process two):
#include "SharedMemory.hpp"
#include "Allocators.hpp"
#include "Pools.hpp"
#include <vector>
#include <iostream>
int main()
{
SharedMemory mem = SharedMemory("Local\\Test_Shared_Memory", 1024);
if (!mem.Open() && !mem.Create())
{
throw std::runtime_error("Error Mapping Shared Memory!");
}
auto pool = PoolAllocator<int, SharedPool<int>>(SharedPool<int>(&mem));
std::vector<int, decltype(pool)> v(pool);
int* ptr = reinterpret_cast<int*>(mem.GetPointer());
std::cout<<"Reading 3 values from: "<<ptr<<"\n";
v.reserve(3);
std::cout<<v[0]<<"\n";
std::cout<<v[1]<<"\n";
std::cout<<v[2]<<"\n";
std::cin.get();
}
This is a tough problem.
One way is to use Shared Memory and when you build the linked list, give it your own allocator, to use the shared memory. Other way is to implement your own linked list based on the shared memory.
You can also try to use boost - Boost interprocess, which is probably the ideal solution.
Specifically speaking - interprocess with containers
I get this error when trying to compile my program:
Field '__jmpbuf' could not be resolved
I looked for a solution for hours and can't seem to find out where is the culprit.
The Thread.h file contains the header of the class. It has the private member:
sigjmp_buf _env;
And the implementation is inside Thread.cpp:
#include "Thread.h"
#include <setjmp.h>
#include "translateAdd.h"
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/time.h>
#define COUNTER_INIT -1
int Thread::_idCounter = COUNTER_INIT;
Thread::Thread(void (*threadsFunc)(void))
: threadsFunction(threadsFunc), _stack(new char[STACK_SIZE]), _quantums(1)
{
address_t sp, pc;
sp = (address_t)_stack + STACK_SIZE - sizeof(address_t);
pc = (address_t)threadsFunc;
// set environment for later return
sigsetjmp(_env, 1);
(_env->__jmpbuf)[JB_SP] = translate_address(sp);
(_env->__jmpbuf)[JB_PC] = translate_address(pc);
sigemptyset(&_env->__saved_mask);
_id = ++_idCounter;
_state = READY;
}
EDIT: Using eclipse as the IDE under ubuntu 32bit
EDIT: Another complete example that doesn't compile on my machine:
#include <stdio.h>
#include <setjmp.h>
#include <signal.h>
#include <unistd.h>
#include <sys/time.h>
#define SECOND 1000000
#define STACK_SIZE 4096
char stack1[STACK_SIZE];
char stack2[STACK_SIZE];
sigjmp_buf env[2];
#ifdef __x86_64__
/* code for 64 bit Intel arch */
typedef unsigned long address_t;
#define JB_SP 6
#define JB_PC 7
/* A translation is required when using an address of a variable.
Use this as a black box in your code. */
address_t translate_address(address_t addr)
{
address_t ret;
asm volatile("xor %%fs:0x30,%0\n"
"rol $0x11,%0\n"
: "=g" (ret)
: "0" (addr));
return ret;
}
#else
/* code for 32 bit Intel arch */
typedef unsigned int address_t;
#define JB_SP 4
#define JB_PC 5
/* A translation is required when using an address of a variable.
Use this as a black box in your code. */
address_t translate_address(address_t addr)
{
address_t ret;
asm volatile("xor %%gs:0x18,%0\n"
"rol $0x9,%0\n"
: "=g" (ret)
: "0" (addr));
return ret;
}
#endif
void switchThreads(void)
{
static int currentThread = 0;
int ret_val = sigsetjmp(env[currentThread],1);
printf("SWITCH: ret_val=%d\n", ret_val);
if (ret_val == 1) {
return;
}
currentThread = 1 - currentThread;
siglongjmp(env[currentThread],1);
}
void f(void)
{
int i = 0;
while(1){
++i;
printf("in f (%d)\n",i);
if (i % 3 == 0) {
printf("f: switching\n");
switchThreads();
}
usleep(SECOND);
}
}
void g(void)
{
int i = 0;
while(1){
++i;
printf("in g (%d)\n",i);
if (i % 5 == 0) {
printf("g: switching\n");
switchThreads();
}
usleep(SECOND);
}
}
void setup(void)
{
address_t sp, pc;
sp = (address_t)stack1 + STACK_SIZE - sizeof(address_t);
pc = (address_t)f;
sigsetjmp(env[0], 1);
(env[0]->__jmpbuf)[JB_SP] = translate_address(sp);
(env[0]->__jmpbuf)[JB_PC] = translate_address(pc);
sigemptyset(&env[0]->__saved_mask);
sp = (address_t)stack2 + STACK_SIZE - sizeof(address_t);
pc = (address_t)g;
sigsetjmp(env[1], 1);
(env[1]->__jmpbuf)[JB_SP] = translate_address(sp);
(env[1]->__jmpbuf)[JB_PC] = translate_address(pc);
sigemptyset(&env[1]->__saved_mask);
}
int main(void)
{
setup();
siglongjmp(env[0], 1);
return 0;
}
If you really need to use the internal fields (which will only be valid for your compiler on your system) you need to check the types:
typedef struct __jmp_buf_tag sigjmp_buf[1];
That means that sigjmp_buf is not a pointer, but an array with a single structure in it. So you use it like a normal array of structures:
sigjmp_buf _env;
_env[0].__jmpbuf[x] = y;
I really recommend against the use the internal field of this structure. Linux have other functions to simplify cooperative threading (which is what you seem to be implementing).
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;
}
};