【问题标题】:How to deal with multiple parameters of different types in C++98?C++98中如何处理多个不同类型的参数?
【发布时间】:2019-09-19 20:45:47
【问题描述】:

为了实现一个线程类(在C++98Windows.h中)。我有这样的事情:

Thread::Thread(_beginthreadex_proc_type fn)
{
    m_raw = fn;
    m_args = 0;
    m_handle = 0;
    m_id = 0;
}

上面的代码可以正常工作,它接受一个不接收参数的函数,并且在下一个代码中,它的函数被一个新线程调用:

void Thread::Join()
{
    m_handle = (HANDLE)_beginthreadex(0, 0, m_raw, (m_args ? m_args : 0), 0, 0);
    if (m_handle) WaitForSingleObject(m_handle, INFINITE);
}

此代码也适用于不带任何参数的函数。 现在我的问题是我如何在 C++98 中接收我的构造函数中的可变参数并保存它们。 NO 如果我不需要帮助,我就不能使用现代 C++。所以请不要给我用 c++11 或更高版本实现的解决方案。

更新

现在我正在尝试一种 Java 风格的解决方案,因为每个线程都是一个 IRunnable,它有一个名为 Run 的纯虚函数。而线程几乎就是这个实现与抽象类的差异。通过这种方式,我可以避免参数,因为我不传递函数,而是编写另一个从 Thread 继承并实现 Run 的类。 代码如下:

界面

struct IRunnable
{
    virtual void Run() = 0;
};

线程类

class Thread : public IRunnable
{
    HANDLE m_handle;
    DWORD  m_id;
    typedef unsigned (__stdcall *Function)(void*);
    _beginthreadex_proc_type m_raw;
    void* m_args;
public:
    Thread();
    ~Thread();
    Thread(_beginthreadex_proc_type, void*);
    Thread(_beginthreadex_proc_type);
    unsigned GetId();
    virtual void Run() = 0;
    void Join();
    unsigned int __stdcall call(void*);
};

Call only 是调用 Run 函数成员的包装器

unsigned int __stdcall Thread::call(void* data)
{
    Run();
    return 0;
}

我的问题在这里:

void Thread::Join()
{
    m_handle = (HANDLE)_beginthreadex(0, 0, &this->call, 0, 0, 0);
    if (m_handle) WaitForSingleObject(m_handle, INFINITE);
}

当我在 vs2019 中编译时,上面的代码会产生下一个错误:

error C2276: '&': illegal operation on bound member function expression

error C2660: '_beginthreadex': function does not take 5 arguments

【问题讨论】:

  • 为什么你想要构造函数的可变参数?您将如何处理这些论点以及您期望什么样的论点?
  • 有很多方法,但没有一个好玩。与其经历那种痛苦,不如直接使用boost::thread?它与 C++98 兼容,并且现代 C++ 的线程库以它为模型,因此稍后升级代码以使用 <thread> 非常简单。
  • 我不能使用外部库是出于教育目的。谢谢
  • 通过存储可变参数可以达到什么目的?它们没有名称,难以使用,没有类型信息,编译器对它们的支持非常有限......相反,您可以将 void* 存储到某些用户定义的结构中,或者用户传递一个实现 operator() 并使用其成员的函子对象。
  • 把你需要的一切都放在一个结构中,并在你创建它的时候把它交给线程

标签: c++ windows winapi c++98


【解决方案1】:

对于您编辑的问题,您收到编译错误的原因是您试图将地址发送到 Thread 对象的成员函数。您不能在不保留对象指针的情况下获取指向成员函数的指针并使用它们。相反,您应该创建一个以 Thread* 作为参数的全局函数,发送指向该函数的指针,然后让它调用您的可运行对象。

unsigned thread_entry(void* thread_ptr)
{
    Thread* thread = (Thread*) thread_ptr;
    return thread->call();
}

void Thread::Join()
{
    m_handle = (HANDLE)_beginthreadex(0, 0, thread_entry, this, 0, 0);
    if (m_handle) WaitForSingleObject(m_handle, INFINITE);
}

附:如果问题与您的问题明显不同,通常最好提出新问题而不是编辑旧问题。

【讨论】:

  • 感谢您阅读您的评论,我的实现完全相同。我的意思是在你输入代码之前 xd
【解决方案2】:

如果您查看几乎所有线程库,它们很少支持发送多个参数;你通常会发送一个指向某个东西的指针,如果你想要很多东西,你可以创建一个包含很多东西的结构并发送一个指向它的指针。

但是,如果你真的想要这个,你可以使用 C 可变参数函数来迭代所有可变参数,并为它们分配一个链表,或者分配它们的数组,或者你想要的任何其他数据结构。然后,发送一个指向你的线程入口函数的指针。不过,您的函数仍将只使用一个指针。

在 C 中,没有简单的方法来构造 va_list,这就是可变参数的发送方式。您不能只发送主线程上的 va_list,因为当它到达您的新线程时,该内存将不再存在。也没有很好的方法来扩展 va_list 来填充函数参数。

顺便说一句,我知道您使用的是 C++,但就 C++98 而言,它对可变参数的支持与 C 中的基本相同,这就是我在回答中提到 C 的原因。

【讨论】:

  • 谢谢。现在我正在尝试一种 Java 风格的解决方案,因为每个线程都是一个 IRunnable,它有一个名为 Run 的纯虚函数。并且线程几乎是这个实现与抽象类的差异。这样我可以避免参数,因为我不传递函数而不是我编写另一个继承自 Thread 并实现 Run 的类
  • @emanuelclur 是的,这是一个更好的方法。我发现公开指定回调和发送指向回调的指针的低级功能也很好。这可能取决于样式以及您的代码库是如何面向对象的。
  • >他们很少支持发送多个参数。 Err... C++ std::thread,支持多个参数就好了,boost::thread 也是如此。我认为唯一不支持它的 C++ 线程库是 Qt (Qthread) - 尽管 QConcurrent 支持。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-11-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-28
  • 1970-01-01
  • 2021-04-04
相关资源
最近更新 更多