3

I have the following code where I create threads based on the for loop below, the 3rd argument in pthread takes a void* ()(void) and I am giving it a string arg. The problem is that every thread has it's own method i.e thread 0 has its method thread0(). I want to be able to set the method based on the t of the for loop without getting the following error:

main2.cpp:40:51: error: cannot convert ‘std::__cxx11::string {aka std::__cxx11::basic_string<char>}’ to ‘void* (*)(void*)’ for argument ‘3’ to ‘int pthread_create(pthread_t*, const pthread_attr_t*, void* (*)(void*), void*)’
 err = pthread_create(&threads[t], &attr,method, &t);
for(t = 0; t <5; t++){;
    printf("Creating thread %d\n",t);
    std::string method = "thread"+t;
    err = pthread_create(&threads[t], &attr,method, &t);
    if(err != 0) exit(-1);
}
Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
mnismael
  • 43
  • 5
  • 2
    You cannot give it a string argument. There's a function pointer expected. – user0042 Nov 10 '17 at 18:13
  • 1
    So, why can't you create an array of function pointers, and pass the pointer to the required function, based on `t`? – Algirdas Preidžius Nov 10 '17 at 18:15
  • @AlgirdasPreidžius Can you guide me with doing that? – mnismael Nov 10 '17 at 18:16
  • 1
    Perhaps read http://en.cppreference.com/w/cpp/thread/thread – Ed Heal Nov 10 '17 at 18:16
  • @EdHeal: It won't really help OP in this case; synthesizing a function pointer from a string is just not possible. OP wants an array of function pointers, or a map of strings to function pointers. – AndyG Nov 10 '17 at 18:18
  • @AndyG - It was a comment that using `std::thread` might be worth a read. – Ed Heal Nov 10 '17 at 18:20
  • @AndyG _"synthesizing a function pointer from a string is just not possible."_ I'm starting to think about tokenizing preprocessor macros. Isn't that how some dispatcher frameworks work? – user0042 Nov 10 '17 at 19:22
  • @user0042: You could use a macro to refer to a function, yeah, but you wouldn't do it by stringizing anything. Besides, OP is creating strings at runtime; there's no way in C++ to go from those directly to a function pointer. – AndyG Nov 10 '17 at 19:30
  • @AndyG Wait, _stringizing_ is the correct term, yes. I was thinking about preprocessor macros to bind function pointers into a map with string key values. Isn't that technique used in MFC and similar frameworks to provide event handler maps? – user0042 Nov 10 '17 at 19:35
  • @user0042: Ah now you're touching on something usable! Yes you could write preprocessor machinery to *map* a string to a function pointer. This is pretty much what the OP needs to do, one way or another. – AndyG Nov 10 '17 at 19:45
  • @AndyG That's basically what _dynamic library interfaces_ do as well. The stub bindings provide exactly that kind of mapping. – user0042 Nov 10 '17 at 19:49

1 Answers1

1

The error message is pretty clear about the function pointer, that you can't translate c++ symbols to strings or vice versa.

Here's the signature of pthread_create() as given in the documentation:

SYNOPSIS

intpthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);

You can do the following:

typedef void* (*start_routine_t)(void*);

void* foo_0(void*) { return nullptr; }
void* foo_1(void*) { return nullptr; }    
void* foo_2(void*) { return nullptr; }
void* foo_3(void*) { return nullptr; }
void* foo_4(void*) { return nullptr; }

constexpr std::map<std::string,start_routine_t> thread_funcs {
    { "thread_0" , foo_0 } ,
    { "thread_1" , foo_1 } ,
    { "thread_2" , foo_2 } ,
    { "thread_3" , foo_3 } ,
    { "thread_4" , foo_4 } ,
};

pthread_t threads[5];


// ....

for(t = 0; t <5; t++){;
    printf("Creating thread %d\n",t);
    std::ostringstream method;
    method << "thread_" <<t;
    err = pthread_create(&threads[t], &attr,method, &thread_funcs[method.str()],nullptr);
    if(err != 0) exit(-1);
}

Or the more straightforward way without using strings at all:

start_routine_t thread_funcs[5] = { foo_0, foo_1, foo_2, foo_3, foo_4 }; 
pthread_t threads[5];

// ...

for(t = 0; t <5; t++){
    printf("Creating thread %d\n",t);
    err = pthread_create(&threads[t], &attr,method, thread_funcs[t], nullptr);
    if(err != 0) exit(-1);
}

As you also asked for facilities:

  1. Use std::thread instead of the pthread-API directly at all. If your target environment supports pthreads properly, you usually can use the std::thread ABI.
  2. Use lambda functions to refer to specific routines on the fly:

    std::vector<std::thread> threads(5);
    for(t = 0; t <5; t++){
       printf("Creating thread %d\n",t);
       auto thread_func = [t]() {
           switch(t) {
           case 0: foo_0(); break;
           case 1: foo_1(); break;
           case 2: foo_2(); break;
           case 3: foo_3(); break;
           case 4: foo_4(); break;
           }               
       };
       threads[t] = std::thread(thread_func);
    }
    

    The above code example probably isn't the best (most efficient), but demonstrates how to map function calls on the fly.

user0042
  • 7,917
  • 3
  • 24
  • 39
  • Maybe the use of `std::function`, `std::thread` would be a better C++ way of doing things – Ed Heal Nov 10 '17 at 18:25
  • @EdHeal Sure. But OP primarily asks how to map strings to function pointers it seems. – user0042 Nov 10 '17 at 18:26
  • @user0042 It gave. me this error: **main2.cpp:57:51: error: cannot convert ‘std::ostringstream {aka std::__cxx11::basic_ostringstream}’ to ‘void* (*)(void*)’ for argument ‘3’ to ‘int pthread_create(pthread_t*, const pthread_attr_t*, void* (*)(void*), void*)’ err = pthread_create(&threads[t], &attr,method, &t);** – mnismael Nov 10 '17 at 18:29
  • @mnismael I fixed that. You should have a look [here](http://man7.org/linux/man-pages/man3/pthread_create.3.html) what's required. – user0042 Nov 10 '17 at 18:35