0

In order to implement a thread class(In C++98 and Windows.h). I have something like this:

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

The code above works fine it takes a function that not receive parameters, and with the next code it function is called by a new thread:

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

This code also works fine with functions that don't take any parameter. Now my question is about how can i in C++98 receive variadic parameters in my constructor and save them. And NO i can't use modern c++ if that was the case I din't need help. So plz don't give me solutions implemented with c++11 or higher.

Update

Now I'm trying a Java style solution in that every Thread is a IRunnable that have a pure virtual function named Run. And thread is almost the that this implementetation with the diff that is an abstract class. In this way can i avoid parameters because I don't pass the function instead of that I write another class that inherits from Thread and implements Run. The code look like:

The interface

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

Thread class

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 is a wrapper to call Run function member

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

My problem is here:

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

When i compiling in vs2019 the code above produce the next error:

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

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

  • 1
    *Why* do you want variadic arguments for constructor? What are you going to do with those arguments and what kind of arguments do you expect? – Yksisarvinen Sep 19 '19 at 20:48
  • 6
    There are ways, none of them fun. Instead of going through that suffering, why not just use `boost::thread`? It is C++98 compatible and modern C++'s thread library is modeled after it so upgrading the code later on to use `` is pretty straight forward. – NathanOliver Sep 19 '19 at 20:53
  • I can't use external libraries is for educational purposes. Thx – emanuelclur Sep 19 '19 at 21:04
  • 1
    What would you achieve by storing variadic arguments? They don't have names, they are difficult to use, they don't have type information, there is very limited support for them by compilers... Instead, you could store a void* to some user-defined struct, or have the user pass a functor object that implements operator() and uses its members. – Kalinovcic Sep 19 '19 at 21:15
  • 3
    Put everything you need in a struct and give that to the thread when you create it – David Heffernan Sep 19 '19 at 21:15
  • You can use templates or if you want only one thread function use a vector of std::any. Since std::any is c++17 you have to mimic it. As another contributor states they are hard to use in client code since any can be anything. Btw 'call' must be a static member function (and pass 'this' as void*) – gast128 Sep 19 '19 at 21:26
  • C++98: The New Turbo C++. Consider splitting function `Join` up. Typically join means "Block until this thread stops running" Implying the thread is already running. If `Join` starts a thread and blocks, the function will be misused by folks expecting the typical behaviour. Plus, what's the point of spinning a thread if the thread that started it instantly blocks and waits for completion? – user4581301 Sep 19 '19 at 21:42

2 Answers2

2

For your edited question, the reason you're getting a compile error is because you're trying to send the address to a member function of your Thread object. You can't take pointers to member functions and use them without also keeping the object pointer around. Instead, you should make a global function that takes a Thread* as its argument, send a pointer to that function, and let it call your runnable.

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

P.S. It's usually best to ask new questions instead of editing old ones if the question is significantly different, which yours is.

Ruben Helsloot
  • 12,582
  • 6
  • 26
  • 49
Kalinovcic
  • 492
  • 3
  • 11
0

If you look at pretty much any thread library, they very rarely support sending multiple arguments; you usually send a pointer to something, and if you want many things, you make a struct containing many things and send a pointer to it.

However, if you really want this, you could use the C varargs functions to iterate over all variadic arguments, and allocate a linked list with them, or allocate an array of them, or whatever other data structure you want. Then, send a pointer to that to your thread entry function. Your function would still be taking just one pointer, though.

In C, there is no easy way to construct a va_list, which is how variadic arguments are sent around. You can't just send the va_list you have on your main thread, because that memory won't be alive by the time it reaches your new thread. There is also no good way to expand a va_list to fill function arguments.

Btw, I realize you're using C++, but as far as C++98 goes, its varargs support is basically the same as in C, which is why I'm mentioning C in my answer.

Kalinovcic
  • 492
  • 3
  • 11
  • Thank you. Now I'm trying a Java style solution in that every Thread is a IRunnable that have a pure virtual function named Run. And thread is almost the that this implementetation with the diff that is an abstract class. In this way can i avoid parameters because I don't pass the function instead of that I write another class that inherits from Thread and implements Run – emanuelclur Sep 19 '19 at 21:17
  • @emanuelclur Yep, that's a better approach. I find it's also good to expose the lower-level functionality of specifying a callback and sending a pointer to the callback. It may depend on style and how object oriented your codebase is. – Kalinovcic Sep 19 '19 at 21:20
  • >they very rarely support sending multiple arguments. Err... C++ std::thread, supports multiple arguments just fine, so does boost::thread. I think the only C++ thread-library that does not support it is Qt (`Qthread`) - although QConcurrent does. – sbabbi Oct 25 '20 at 17:11