4

I'm writing a C program (myapp) which needs to use a particular API; the API is written in C++. I've worked with C and C++, but never both at once, and I'm getting confused.

So, the API provides the following directory, which I've placed in a folder called include, at the same level as my makefile:

libmyapi.a
api/api.h

My main source file is src/myapp.c, and it includes the API using #include "api/api.h".

My make command is (plus some flags, which I haven't listed because I don't think they're relevant here):

gcc  -Linclude -lmyapi -Iinclude  src/myapp.c -o lib/myapp.sp -lrt

The problem I'm having is that the api.h file contains references to namespaces etc. Eg at one point it has:

namespace MyAPI {
  namespace API {
    typedef SimpleProxyServer SimpleConnection;
  }
}

and obviously the C compiler doesn't know what this means.

So, I assumed I'd need to compile using a C++ compiler, but then someone said I didn't, and I could just "wrap" the code in "extern 'C'", but I don't really understand. Having read around online, I'm not any further on.

Do I need to compile in C++ (ie using g++)?

Do I need to "wrap" the code, and what does that mean? Do I just do:

#ifdef __cplusplus
  extern "C" {
    namespace MyAPI {
      namespace API {
        typedef SimpleProxyServer SimpleConnection;
      }
    }
  }
#endif

or do I just wrap the lines

namespace MyAPI {
      namespace API {

and then their corresponding }}?

The header file calls other header files, so potentially I'll need to do this in quite a lot of places.

So far I've got errors and warnings with all the variations I've tried, but I don't know whether I'm doing the wrapping wrong, setting g++ compiler flags wrong, using the wrong compiler, or what! If I know the method to use, I can at least start debugging.

halfer
  • 19,824
  • 17
  • 99
  • 186
Sharon
  • 3,471
  • 13
  • 60
  • 93
  • 1
    Does this answer your question? [What is the effect of extern "C" in C++?](https://stackoverflow.com/questions/1041866/what-is-the-effect-of-extern-c-in-c) – Simon Kraemer May 26 '20 at 10:58
  • 1
    Check out the example given in this answer: https://stackoverflow.com/a/30526795/4181011 . I think that it covers all of your questions. – Simon Kraemer May 26 '20 at 10:59
  • 1
    It is probably easier to write an entirely new, "C compatible", interface than to try and reshape the C++ code. If the library isn't already prepared for using from C, it's unlikely that selectively sprinkling `extern "C"` over it will succeed. – molbdnilo May 26 '20 at 11:00
  • 1
    Does this answer your question? [How to call C++ function from C?](https://stackoverflow.com/questions/2744181/how-to-call-c-function-from-c) – moooeeeep May 26 '20 at 11:30
  • 1
    Also related: https://stackoverflow.com/questions/2045774/developing-c-wrapper-api-for-object-oriented-c-code and https://isocpp.org/wiki/faq/mixing-c-and-cpp#call-cpp – moooeeeep May 26 '20 at 11:30
  • Thanks for the links. I've read through them, but still not certain what I'm doing. I'm following Mestkon's answer below, which seems to be close to working. – Sharon May 26 '20 at 12:31

4 Answers4

8

You can write a small C++ program that creates a C binding for the API.

Gvien this API:

namespace MyAPI {
  namespace API {
    typedef SimpleProxyServer SimpleConnection;
  }
}

you can create c_api.h

#ifdef __cplusplus
extern "C" {
#endif

struct api_handle_t;
typedef struct api_handle_t* api_handle;
api_handle myapi_api_create();
void myapi_api_some_function_using_api(api_handle h);
void myapi_api_destroy(api_handle h);

#ifdef __cplusplus
}
#endif

and c_api.cpp

#include "c_api.h"
#include <myapi/api/stuff.hpp>

struct api_handle_t
{
    MyAPI::API::SimpleConnection c;
};

api_handle myapi_api_create()
{
    return new api_handle_t;
}

void myapi_api_some_function_using_api(api_handle h)
{ 
    //implement using h
}

void myapi_api_destroy(api_handle h) 
{
    delete h;
}

compile that with a C++ compiler and include the c_api.h file in the C project and link to the library you created with the C++ compiler and the original library.

Mestkon
  • 3,532
  • 7
  • 18
  • Thank you, this is really helpful. – Sharon May 26 '20 at 11:25
  • Can I ask another question? I've created c_api.cpp and include/c_api.h as outlined above. I have the original libmyapi.a in include/libs. Then I've compiled them into lapiwrapper.so using g++ -Linclude/libs -lmyapi -shared -fPIC -D_LINUX_X86_64 -Iinclude c_api.cpp -o include/libs/lapiwrapper.so -lrt - is that correct? So I now have include/libs/lapiwrapper.so. – Sharon May 26 '20 at 12:22
  • So now I go to my original code, and add "include c_api.h" into one of the headers, and compile with: gcc -Linclude -lmyapi -lapiwrapper -shared -DKXVER=3 -fPIC -D_LINUX_X86_64 -Iinclude src/myapp.c -o lib/myapp.so - this compiles with an error saying: error: unknown type name ‘api_handle_t’. So it's finding the h file, but not the new library. Any ideas what I'm doing wrong? – Sharon May 26 '20 at 12:27
  • 1
    Hmm, you might need to write `typedef struct api_handle_t* api_handle;` C is picky and wont resolve struct names without explicitly writing "struct" in front of them. – Mestkon May 26 '20 at 12:42
  • Thanks, but it didn't like that either. It said it was ignoring the "typedef". – Sharon May 26 '20 at 20:06
  • 1
    Then you did it wrong. Thats a message you get when you're missing the last part of the `typedef` (e.g. `typedef struct api_handle_t*`) – Mestkon May 26 '20 at 21:34
  • You were right, I'd done it wrong - got it working now! Thank you so much for your help! – Sharon May 27 '20 at 08:46
  • I have a follow-up question here: https://stackoverflow.com/questions/62067164/when-accessing-c-api-from-c-via-a-wrapper-how-do-i-access-enum-types - just in case you fancy explaining a bit more! – Sharon May 28 '20 at 14:26
5

Basically, your C++ library needs to export a pure C API. That is, it must provide an interface that relies solely on typedef, struct, enum, preprocessor directives/macros (and maybe a few things I forgot to mention, it must all be valid C code, though). Without such an interface, you cannot link C code with a C++ library.

The header of this pure C API needs to be compilable both with a C and a C++ compiler, however, when you compile it as C++, you must tell the C++ compiler that it is a C interface. That is why you need to wrap the entire API within

extern "C" {
    //C API
}

when compiling as C++. However, that is not C code at all, so you must hide the extern "C" from the C compiler. This is done by adding the preprocessor directives

#ifdef __cplusplus1
extern "C" {
#endif

    //C API

#ifdef __cplusplus1
}
#endif

If you cannot change your libraries header, you need to create a wrapper API that offers this pure C API and calls through to the respective C++ code.

cmaster - reinstate monica
  • 38,891
  • 9
  • 62
  • 106
3

How do I call C++ functions from C?

By writing calling functions whose declarations are valid in the common subset of C and C++. And by declaring the functions with C language linkage in C++.

The problem I'm having is that the api.h file contains references to namespaces

Such header is not written in common subset of C and C++, and therefore it cannot be used from C. You need to write a header which is valid C in order to use it in C.

Do I need to compile in C++ (ie using g++)?

If you have function definitions written in C++, then you need to compile those C++ functions with a C++ compiler. If you have C functions calling those C++ functions, then you need to compile those C functions with C compiler.


A minimal example:

// C++
#include <iostream>

extern "C" void function_in_cpp() {
    std::cout << "Greetings from C++\n";
}

// C
void function_in_cpp(void);

void function_in_c(void) {
    function_in_cpp();
}
eerorika
  • 232,697
  • 12
  • 197
  • 326
0

You cannot. You can use C functions in your C++ program. But you cannot use C++ stuff from C. When C++ was invented, it allowed for compatibility and reuse of C functions, so it was written as a superset of C, allowing C++ to call all the C library functions.

But the reverse is not true. When C was invented, there was no C++ language defined.

The only way you can call C++ functions is to convert your whole project into a C++ one... you need to compile your C functions with a C++ compiler (or a C compiler if they are plain C) but for a C function to call a C++ function it must be compiled as C++. You should declare it with:

extern "C" {

void my_glue_func(type1 param1, type2 param2, ...)
{
    /* ... */
}

} /* extern "C" */

and link the whole thing as a C++ program (calling the c++ linker)

This is because C doesn't know anything about function overloading, class initializacion, instance constructor calls, etc. So if you even can demangle the names of the C++ functions to be able to call them from C (you had better not to try this), they will probably run uninitialized, so your program may (most) probably crash.

If your main() function happens to be a C function, then there's no problem. C++ was designed with this thing in mind, and so, main() is declared implicitly as extern "C". :)

Luis Colorado
  • 10,974
  • 1
  • 16
  • 31