0

I have a class that handles all communication with a particular piece of hardware. The hardware requires data updates via one socket, and broadcasts its IP for handshaking purposes on a known (and hardcoded) multicast address.

As a result, the class spawns two threads, one thread which handles direct communication with the hardware and updates the data based on a member structure of the class. The other thread continuously monitors the multicast address to see if the IP has changed.

Both threads are implemented as static member functions inside the class. However, in order to use pthreads, my understanding is that they need to be declared as extern "C." That said, based on my research it is not possible to declare member functions of a class as extern "C." The way I saw some code from the engineer who worked on the project previously get around this problem is that the threaded function implementation is surrounded in extern "C." That is, extern "C" is not found in the function declaration or definition, but the whole function definition is encapsulated in extern "C" {}.

Like this:

extern "C" {
  // function goes here
}

I have several questions about this implementation as I look to improve on my understanding of the code and become better at multithreaded programming.

  1. Does the extern "C" actually do anything given the way it is implemented? I've read about what the modifier actually does here: In C++ source, what is the effect of extern "C"? but in this case is it actually applicable given the fact that we are dealing with functions that are static members of a class?

  2. The threaded member functions seem to get around the fact of modifying the class's member variables by doing a reinterpret_cast and creating a pointer to the class. Is this standard practice, because my red flag was raised when I saw that cast?

  3. The constructor calls pthread_create() to launch the threads. I was planning on doing something like adding a static member variable which only allows one instance to communicate with the hardware at any given time. That is, I was thinking about allowing the caller to have multiple instances of the class, but only one "connected" at a time. Is this standard practice, or is there something "hacky" in my way of thinking? There will only ever be one piece of hardware attached at a given time, so there is only the requirement to have one class instance operational at a given time.

Thanks for all your help. Also, if you are just going to suggest something like "Dude, just use Boost threads, FTW," please save your breath. Although I will eventually move to a threading library, my understanding is that Boost just wraps pthreads on POSIX systems. As a result, I want to learn about using the raw threads first before I spoil myself with a library. While my main goal is delivering on time, no one said that I couldn't learn anything along the way. ;)

Edited to add:

Here's some source code describing the issue that I'm seeing for clarity. I'm still not 100% sure that the extern "C" is doing anything in this case...

In my .h:

class MyClass
{
 private:
  static void* ThreadFunc(void* args);  // extern "C" not found in declaration
  static bool instance_;
};

In my .cpp:

MyClass::MyClass() {
  if (!instance_) {
    instance_ = true;
    // spawn threads here
  } else {
    // don't spawn threads and warn user
  }
}
extern "C" {
  void *MyClass::ThreadFunc(void* args) {  //definintion wrapped in extern C
    MyClass* myclass_ptr = static_cast<MyClass*>(args);
    // ... more code
    return static_cast<void*> 0;
  }
Community
  • 1
  • 1
It'sPete
  • 5,083
  • 8
  • 39
  • 72
  • 1
    You do not gain anything with the changes you have made. From $7.5/4 - _A C language linkage is ignored in determining the language linkage of the names of class members and the function type of class member functions._ – Captain Obvlious Jun 18 '13 at 00:09
  • Thanks. So the bracketizing extern "C" does nothing as I suspected. So, is there a "correct" way to spawn threads from a class? Can I just rid myself of extern "C" and call it good? – It'sPete Jun 18 '13 at 01:21
  • 1
    Based on my understanding of the topic and the information I included in my answer simply passing a pointer to the member function without using `extern "C"` is _one_ correct way of doing it (see my notes on the implicit cast). Casey provides a portable, correct and IMO preferred approach in his answer. – Captain Obvlious Jun 18 '13 at 02:21
  • 1
    Thanks, from what I've gathered, just passing a static member function does work simply because g++ uses the same conventions for Extern C as is does static member functions. While I do like Casey's implementation, my main problem is that I want the spawn threads to be private. Therefore, the client only needs to create the object and they have all the connectivity they want to the device abstracted from them. I did remove the Extern C since it was just being ignored. I think I'm OK with the lack of portability since I am soley targetting an embedded Linux distro – It'sPete Jun 18 '13 at 21:22
  • I also like Casey's implementation. I just cant figure out how to modify it for a template class. If I cant figure out how to do the modification, I am going to tell people to use clang++ and g++ since I know they compile the code correctly. – Zachary Kraus Aug 27 '14 at 20:28

2 Answers2

3

Question #1

is it actually applicable given the fact that we are dealing with functions that are static members of a class?

It is applicable but not necessary. A conforming implementation (compiler) is aware of the relevant type information to either use the function pointer as-is, perform an implicit conversion or fail with an error.

Keep in mind though that additional properties can be applied when specifying a language linkage. This may include but is not limited to a specific calling convention. The standard leaves this up to the implementation however.

From $7.5/1

Some of the properties associated with an entity with language linkage are specific to each implementation and are not described here. For example, a particular language linkage may be associated with a particular form of representing names of objects and functions with external linkage, or with a particular calling convention, etc.

By specifying a language linkage you let the implementation deal with the specifics of those properties. Otherwise you would need to use language extensions to specify things like the calling convention. Even if you do that there may be other properties that are either unknown or cannot be applied through implementation specific extensions.

The language linkage also affects the type of an entity, in this case a function.

From $7.5/1

Two function types with different language linkages are distinct types even if they are otherwise identical.

In your example the pointer to the static member function is convertible to the function pointer type of the thread start function. This is why your code works properly without specifying a language linkage and without an explicit cast.

This doesn't mean you should not specify a language linkage though. If you are concerned about portability between implementations it's a good idea to use it. For instance some implementations use fastcall as the default C calling convention where others use cdecl as the default. In the absence of a language linkage specifier the code would not compile, at least not with a conforming implementation.

The example below demonstrates how the conversion will fail if different calling conventions are used. I chose using calling conventions as they are the easiest to express in an example.

#include <iostream>

#if defined(__GNUC__)
#define FASTCALL __attribute__((fastcall))
#elif defined(_MSC_VER)
#define FASTCALL __fastcall
#endif

extern "C" 
{
    typedef void (*FUNC1)(); // Default C calling convention
    typedef void (FASTCALL *FUNC2)();  // fastcall calling convention
    void do1(FUNC1 f) { f(); }
    void do2(FUNC2 f) { f(); }
}

struct X
{
    static void test() {}
};

int main()
{
    do1(X::test);   // Ok. Implicit conversion available
    do2(X::test);   // Fail! Incompatible types. No conversion available
}

Question #2

... modifying the class's member variables by doing a reinterpret_cast and creating a pointer to the class. Is this standard practice, because my red flag was raised when I saw that cast?

Using a cast is the only way to accomplish this but using reinterpret_cast is not necessary and IMO should definitely be avoided. The thread start function passed to pthread_create takes a void pointer as it's only argument. static_cast is sufficient and much narrower in behavior.

Question #3

I was planning on doing something like adding a static member variable...

The question is subjective and how you approach this is entirely up to you. Without some code it's hard to tell whether your implementation is a hack.

As a final note...DUDE! Use teh Boost yo! :)

Captain Obvlious
  • 19,754
  • 5
  • 44
  • 74
  • 3
    Although it works correctly in every implementation I have used, it is technically undefined behavior to call a function through a pointer with different language linkage (5.2.2 [expr.call] p1). So yes, the standard requires you to pass `extern C` function pointers to `pthread_create`. – Casey Jun 15 '13 at 04:04
  • @Casey 5.2.2/1 would apply if the call was being made _from_ C++. In this case it is being called from C which has no concept of language linkage. The only thing that is occurring in C++ is an implicit conversion between pointer types. At most the behavior is implementation-defined. – Captain Obvlious Jun 15 '13 at 22:24
  • Thanks for your help. See above for an edit with the source code. – It'sPete Jun 17 '13 at 20:16
  • @CaptainObvlious thank you for that explanation. That is how I thought the compiler was handling the function pointer calls which also explains why on most compilers you dont need to use extern "C" when using pthreads. – Zachary Kraus Aug 27 '14 at 19:53
  • @CaptainObvlious, ISO C++ says there are no implicit conversions between different function pointer types. _"In your example the pointer to the static member function is convertible to the function pointer type of the thread start function. "_ This is incorrect. In the case of G++ the compiler does not convert anything because it is already the correct type (because G++ does not treat language linkage as part of a function's type, which is a bug). – Jonathan Wakely Aug 28 '14 at 11:11
2

You can't declare a static member function extern C, but you can call a static member function from another function that is extern C. In the cpp file where your thread function is defined:

namespace {
extern "C" {
void* some_thread_func(void* arg) {
  return MyClass::static_thread_func(arg);
}
}}

// static member
// (You can omit this "middle-man" if "do_stuff()" is public.)
void* MyClass::static_thread_func(void* arg) {
  return static_cast<MyClass*>(arg)->do_stuff();
}

void MyClass::start_thread_func() {
  pthread_t thread;
  int error = pthread_create(&thread, NULL, some_thread_func, this);
  // handle errors, store "thread" somewhere appropriate.
  ...
}

The multiple instances / one connected question is a design issue and I don't know what design really fits the rest of your program - but I will say that Singleton seems to reflect your design requirement that there is a single device to manage.

Finally, don't use boost::thread, use std::thread ;)

Casey
  • 41,449
  • 7
  • 95
  • 125
  • How do I modify some_thread_func if MyClass has a template parameter to it? – Zachary Kraus Aug 27 '14 at 20:16
  • @ZacharyKraus There's really no good way, since a template cannot have C language linkage (N3936 [temp]/4). To be language-lawyer correct, you must define a distinct `extern "C"` function corresponding to each specialization of the template class. Personally, I would take the portability hit and pass a static class member function directly to `pthread_create` if I couldn't avoid pthreads altogether and simply use `std::thread` / `boost::thread`. – Casey Aug 27 '14 at 22:48