Question

Alright, first of all so far this works, but is extremely buggy. I want it to be able to take ints, floats, doubles, strings, and char*s. It sort of works by trying everything as a char* but if that fails i would like it retry it as another type. I would also like if i didn't have to pass in the number of params. (more at bottom)

#include <iostream>
#include <cstdlib>
#include <sstream>
#include <iostream>
#include <windows.h>
#include <ctime>
#include <tchar.h>
#include <stdio.h>
#include <vector>
#include <thread>
const enum loglevel{INFO,WARNING,OK,SEVERE};
void logHelperMessage(loglevel,int, ...);
void threadedloghelpermessage(loglevel,string);

int main(int argc, char **argv)
{
    logHelperMessage(INFO,4,"Hi","I","DO","WORK");
}

void logHelperMessage(loglevel severity,int number, ...)
{
    va_list messages;
    va_start(messages,number);
    std::stringstream ss;

    for(int i = 0;i < number;i++)
    {
            ss << va_arg(messages,char*);
    }
    std::string s = ss.str();
    thread t1(threadedloghelpermessage,severity,s);
    t1.join();
}

void threadedloghelpermessage(loglevel severity,string message)
{
    //TODO: implement a stack?
    switch (severity)
    {
    case INFO:
        SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_BLUE);
        cout << "[IF]";
        break;
    case WARNING:
        SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),0x06);
        cout << "[WA]";
        break;
    case OK:
        SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_GREEN);
        cout << "[OK]";
        break;
    case SEVERE:
        SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_RED);
        cout << "[ER]";
        break;
    default:
        break;
    }
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),0x08);
    time_t t = time(0);
    struct tm now;
    localtime_s(&now, &t );
    cout << "[";
    int hour = now.tm_hour;
    if(hour < 10)
    {
        cout << 0 << hour << ":";
    }
    else
    {
        cout << hour << ":";
    }
    int minu = now.tm_min;
    if(minu < 10)
    {
        cout << 0 << minu << ":";
    }
    else
    {
        cout << minu << ":";
    }
    int sec = now.tm_sec;
    if(sec < 10)
    {
        cout << 0 << sec;
    }
    else
    {
        cout << sec;
    }
    cout << "] ";
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),0x07);
    cout << message << endl;
}

Now,is there anyway to:

  1. Have the threads output to the console without them crashing or hanging the main program as the thread rejoins (threadpool?)
  2. Use: logHelperMessage(logLevel,firstpram,...) (use the first param to get its mem position and go from there?)
  3. In the: ss << va_arg(messages,char*); if it fails to work as a char* possibly try something else?

I looked around for more advanced varidic functions, but it seems all of them require a pramiter of the number of arguments. or only allowing one type. Also, if a continuous loop is needed, I have a loop set up somewhere else in the program. (I think that's everything)

Was it helpful?

Solution

If C++11 is an option for you (and considering the inclusion of the <thread> header, this seems to be the case), you could use variadic templates instead of C-style variadic functions.

Something like this should do the job (can't test it right now, so please tell me if it doesn't work and I'll try to fix it):

template<typename T>
void insert_messages(std::stringstream& ss, T&& arg)
{
    ss << std::forward<T>(arg);
}

template<typename T, typename... Ts>
void insert_messages(std::stringstream& ss, T&& arg, Ts&&... args)
{
    ss << std::forward<T>(arg);
    logMessage(std::forward<Ts>(args)...);
}

template<typename... Ts>
void logHelperMessage(loglevel severity, Ts&&... args)
{
    std::stringstream ss;
    insert_messages(ss, std::forward<Ts>(args)...);

    std::string s = ss.str();
    std::thread t1(threadedloghelpermessage,severity,s);
    t1.join();
}

OTHER TIPS

Variadic functions can take different type of variables, however. The function itself must Know exactly of which type each argument is. Take a look at printf() for example. You can pass strings, integers, pointes, etc. to it, and the format string tells the function exactly what type each variable is.

Regarding number of arguments, this is a feature of C99. AFAIK Visual studio compiler doesn't support it.

You can find example for C99 in this question.

In C++11, this is easy:

First, some helper functions:

void do_in_order() {};
template<typename Lambda0, typename... Lambdas>
void do_in_order( Lambda0&& L0, Lambdas&&... Ls ) {
  std::forward<Lambda0>(L0)();
  do_in_order( std::forward<Lambdas>(Ls)... );
}

do_in_order takes a variardic set of nullary lambdas, and runs them in order.

Next, logHelperMessage:

template<typename... Args>
void logHelperMessage(loglevel severity,Args&&... args) {
  std::stringstream ss;

  do_in_order( [&](){
    ss << std::forward<Args>(args);
  }...);
  std::string s = ss.str();
  thread t1(threadedloghelpermessage,severity,s);
  t1.join();
}

and done. Most of the heavy lifting is done by do_in_order, where we package up a bunch of tasks to stuff each arg into the stringstream one at a time.

Personally, I wouldn't use this design, because launching a thread and immediately joining it isn't much of an improvement to doing it inline.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top