【问题标题】:A variadic function with different types, passing to a threaded cout具有不同类型的可变参数函数,传递给线程 cout
【发布时间】:2013-03-29 20:34:48
【问题描述】:

好的,首先到目前为止,这是可行的,但是非常有问题。我希望它能够接收intsfloatsdoublesstringschar*s。它通过将所有内容都尝试为 char* 来工作,但如果失败,我希望它作为另一种类型重试。我也想如果我不必传递参数的数量。 (更多在底部)

#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;
}

现在,有没有办法:

  1. 让线程输出到控制台,而不会在线程重新加入时使主程序崩溃或挂起(线程池?)
  2. 使用:logHelperMessage(logLevel,firstpram,...)(使用第一个参数获取它的内存位置并从那里去?)
  3. 在:ss &lt;&lt; va_arg(messages,char*);,如果它不能作为char* 工作,是否可以尝试其他方法?

我四处寻找更高级的可变函数,但似乎它们都需要参数数量的参数。或只允许一种类型。 另外,如果需要连续循环,我会在程序的其他地方设置一个循环。 (我认为这就是一切)

【问题讨论】:

    标签: c++ multithreading function variadic


    【解决方案1】:

    如果您可以选择 C++11(并且考虑到包含 &lt;thread&gt; 标头,这似乎是这种情况),您可以使用 use variadic templates instead of C-style variadic functions

    这样的东西应该可以完成工作(现在无法测试,所以如果它不起作用,请告诉我,我会尝试修复它):

    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();
    }
    

    【讨论】:

    • 那只能输入一种婴儿车吗? IE:所有字符串,或所有整数。
    • @StevenVenham:你的意思是,它应该检查所有参数是否属于同一类型?
    • 不,我的意思是因为它是一个 T 类型,它如何接受参数并将它们用作那里的 T?例如,如果我通过:"Hi",1,3.4,'O' 是否会理解存在多种不同类型的参数?
    • @StevenVenham:是的,可变参数模板是类型安全的,您可以传入任何类型的对象,而不必担心未定义的行为。其实C风格的可变参数函数are basically made obsolete by C++11's variadic templates.
    【解决方案2】:

    然而,可变参数函数可以采用不同类型的变量。 函数本身必须确切地知道每个参数是哪种类型。 以 printf() 为例。 你可以向它传递字符串、整数、指针等,格式字符串告诉函数每个变量是什么类型。

    关于参数的数量,这是 C99 的一个特性。 AFAIK Visual Studio 编译器不支持它。

    您可以在 question 中找到 C99 的示例。

    【讨论】:

    • 好的,因为我使用 vs2012 我无法做到这一点?
    【解决方案3】:

    在 C++11 中,这很简单:

    首先,一些辅助函数:

    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 采用一组可变的 nullary lambda,并按顺序运行它们。

    接下来,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();
    }
    

    然后完成。大部分繁重的工作都由 do_in_order 完成,我们将一堆任务打包,一次将每个 arg 填充到 stringstream 中。

    就我个人而言,我不会使用这种设计,因为启动一个线程并立即加入它对内联并没有太大的改进。

    【讨论】:

    • template&lt;typename... F&gt; void execute_all(F&amp;&amp;... f) { using T = char[]; T { '\0', (std::forward&lt;F&gt;(f)(), void(), '\0')... }; }
    • @Yakk Iv 在发布此代码后添加了一些代码,该代码利用 bool 来显示线程是否完成,并且我使用信息向量来存储消息。所以 .join 在 if 语句中。
    • @LucDanton 是吗?那也行。它的工作原理和字符数量都比较模糊。 :)
    猜你喜欢
    • 2023-03-08
    • 2012-10-27
    • 2023-04-02
    • 2011-04-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多