Call function outside of current namespace in C++ - c++

I'm trying to call a function defined in a C file from my CPP code and I think I am having issues getting the correct namespace. When compiling I get the error: "Undefined reference to 'Get'".
My C header:
// c.h
#ifndef C_H
#define C_H
#ifdef __cplusplus
extern "C" {
#endif
typedef enum
{
VAL_A1,
VAL_A2
} TYPE_A;
typedef enum
{
VAL_B1,
VAL_B2
} TYPE_B;
typedef enum
{
VAL_C1,
VAL_C2
} TYPE_C;
typedef struct
{
TYPE_B b;
TYPE_C c;
} TYPE_D;
TYPE_A Get(TYPE_B b, TYPE_D *d);
#ifdef __cplusplus
}
#endif
#endif
And my CPP file:
// main.cpp
...
extern "C" {
#include "c.h"
}
...
namespace MyNamespace
{
...
MyClass::MyFunc()
{
TYPE_D d;
// None of these calls will compile
// Get(VAL_B1, &d);
// ::Get(VAL_B1, &d);
}
...
}
I have tried calling without namespace reference and also with the "root" namespace using "::" with no luck. Any help is appreciated. I've read through this which seems to clarify it but I don't really understand it:
using C++ with namespace in C

"Undefined reference" means that the function has been declared (in the header), but not defined. You'll need to define the function in a source file somewhere (presumably the C file you refer to), and make sure that is linked when you build the program.

First, let's note what that error means. An undefined reference at the linker stage means that the compiler is unable to find the instance of something. In this case, the implementation of a function.
Let's look at your code.. There are a few things missing that we need to add to make it compilable:
A definition for Get().
main()
The class definition for MyClass.
Once we added those three fixes, the code compiles without error.
extern "C" { extern "C" {
typedef enum {
VAL_A1,
VAL_A2
} TYPE_A;
typedef enum {
VAL_B1,
VAL_B2
} TYPE_B;
typedef enum {
VAL_C1,
VAL_C2
} TYPE_C;
typedef struct {
TYPE_B b;
TYPE_C c;
} TYPE_D;
TYPE_A Get(TYPE_B b, TYPE_D *d) {
return VAL_A1;
}
}}
namespace MyNamespace {
struct MyClass {
void MyFunc();
};
void MyClass::MyFunc() {
TYPE_D d;
Get(VAL_B1, &d);
::Get(VAL_B1, &d);
}
}
int main() {}

The definition of Get (not shown in the question) also needs to be enclosed in extern "C".
The main difference between C and C++ functions, in practice, is the way they are named in the executable format. C++ functions get "name mangling" treatment by the linker but C functions do not. The linker will see the C++ definition of Get and it will have no idea of its relation to the C declaration, even if they have the same signature.

Related

ODR and C++ Versioning for Shared Library Wrapper

For simplicity, I will omit things like proper typedefs to opaque structs instead of void *, Windows calling conventions, fixed integer types, etc.
Suppose I have the following files:
CApi.h -- Shared library header with C linkage for portability and hiding proprietary code.
#define LIB_API // library import/export details
extern "C" {
typedef int error;
error LIB_API lib_foo_create(void ** foo);
error LIB_API lib_foo_destroy(void * foo);
error LIB_API lib_foo_func(void * foo, int * out);
error LIB_API lib_bar_create(void ** bar);
error LIB_API lib_bar_destroy(void * bar);
error LIB_API lib_bar_func(void * bar, int * out); // Suppose this internally uses Foo::func from CxxApi.hpp with C++17
} // extern C
CxxApi.hpp -- Header only wrapper to simplify API usage.
#include "CApi.h"
namespace lib {
namespace detail {
template < typename Return = void, typename Func, typename... Args >
Return c_api(Func func)
{
// Not sure how this affects ODR if client and provider code use different versions,
// but neither see each other's code usage.
#if __cplusplus >= 201703L // C++17
// more efficient implmentation (e.g. fold expressions)
#else
// fallback implmentation (e.g. recursion)
#endif
}
} // namespace lib::detail
class Foo
{
public:
Foo() { detail::c_api(lib_foo_create, &handle_); }
~Foo() { detail::c_api(lib_foo_destroy, handle_); }
int func() { return detail::c_api<int>(lib_foo_func, handle_); }
private:
void * handle_;
};
struct Bar
{
public:
Bar() { detail::c_api(lib_bar_create, &handle_); }
~Bar() { detail::c_api(lib_bar_destroy, handle_); }
int func() { return detail::c_api<int>(lib_bar_func, handle_); }
private:
void * handle_;
};
} // namespace lib
If I, the API provider, compile this using C++17, but the client using the API uses C++11, is ODR violated with lib::detail::c_api?
I believe it is not because lib_bar_func's definition is in a different translation unit than client code, but I am not positive.

Wrapper function for template<> in C++

I am currently trying to create a wrapper interface that can translate C++ to C, and while studying on the possibilities to do that, I came across the template functions (and classes). Knowing that these functions can take any data type and return any data type as well, I find it hard to create a corresponding caller function name that C could read. A simple example is the adder.
template <class typesToAdd>
typesToAdd addStuff(typesToAdd a, typesToAdd b) {
return a + b;
}
My interface includes the extern "C" command to avoid the C++ name mangling in C.
Template is not a function that works for any data type. It is a template for a function that is created at compilation time. Each type you use creates a new function with a new symbol in the binary.
To export to C, you will have to specialize the type you want to use from C, like:
template <class typesToAdd>
typesToAdd addStuff(typesToAdd a, typesToAdd b) {
return a + b;
}
extern "C" {
int addStuffInt(int a, int b) {
return addStuff(a, b);
}
}
You may use function templates to simplify both implementing and maintaining the actual code that does the work but to provide a C interface, you'll still need to explicitly instantiate the functions for the types you aim to support.
If you'd like a C++ user to have the same restricted access to the functions as a C user will have, you can move the template implementation into the .cpp file and do the explicit instantiation there. A C++ user trying to use the function with types for which you have not explicitly instantiated the template will get a linking error.
It could look something like this:
// a.hpp
#pragma once
template <class T>
T addStuff(const T& a, const T& b); // no implementation here.
// a.cpp
#include "a.hpp"
#include "a.h"
template <class T>
T addStuff(const T& a, const T& b) {
T rv = a;
rv += b;
return rv;
}
// C interface - note: it's inside the .cpp file
extern "C" {
int add_ints(int a, int b) {
return addStuff(a, b);
}
double add_doubles(double a, double b) {
return addStuff(a, b);
}
}
/* a.h */
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
int add_ints(int, int);
double add_doubles(double, double);
#ifdef __cplusplus
}
#endif
A C user can now include the .h file and call the two functions for which you've provided an implementation.
Example:
// main.c
#include "a.h"
#include <stdio.h>
int main() {
printf("%d\n", add_ints(10, 20));
printf("%f\n", add_doubles(3., .14159));
}
Compilation:
g++ -c a.cpp
gcc -o main main.c a.o

wrapper function in throwing error " as ā€˜pā€™ was not declared in this scop"

I am trying to access C++ function (f1) and string a from c file using a wrapper function. Code below.
Error thrown is
Error : error: ā€˜pā€™ was not declared in this scope
double d = f11( p,i);
1.h
double f11(struct c* p, int i);
1.cpp
#include<iostream>
using namespace std;
class c
{
public: double f1(int i) // how can i access from c
{
cout<<"I am in c++";
}
public : string a; // how can i access string from c
};
extern "C" double f11(c* p, int i) // wrapper function
{
return p->f1(i);
}
2.c
#include<stdio.h>
#include "1.h"
int main()
{
int i=9;
double d = f11( p,i);
}
If you manually include the contents of "1.h" in main.cpp, it would look:
#include <stdio.h>
double f11(struct c* p, int i);
int main()
{
int i=9;
double d = f11( p,i);
}
There are several problems there.
You haven't declared p before using it in the call to f11.
You don't have any way of constructing an object of type struct c in main. Even if you were to fix the compiler errors by providing declarations of struct c and p, you'll run into run time problems since the only way to initialize p will be to initialize it to NULL. That wouldn't do you any good since you have a line
return p->f1(i);
in f11.
Your declaration and definition of f11 will result in linker error. If you want to implement the function as extern "C", you'll also have to declare it as extern "C".
extern "C" double f11(c* p, int i);
In 1.cpp, the member function f1 does not return a double. That is cause for undefined error, if the compiler does not report that as an error.
See working code at http://ideone.com/aVFWFJ. Please note that I changed the implementation of c::f1 so it does not crash.

Can I use shared library created in C++ in a C program?

I am creating programs using C. However, I require to use a lot of libraries that have API's only for C++. So, is it possible that I can create a shared object in C++ and then access its functionality using C?
The only data I would be passing and returning would be C compatible data types.
Converting or migrating to cpp is not an option here.
If it is not possible to interface these codes, how do I get information from C++ code to C code?
I tried calling C++ functions from C, but I get errors during linking when I include <string>. So when I call C++ functions from C, should I only use that code which will be C compiler compatible?
C++ header cppfile.hpp
#ifndef CPPFILE_H
#define CPPFILE_H
#ifdef __cplusplus
extern "C" {
#endif
extern int myfunction(const char *filename);
#ifdef __cplusplus
}
#endif
#endif
C++ file cppfile.cpp
#include "cppfile.hpp"
#include <string>
int myfunction(const char *filename) {
String S(filename);
return 0;
}
C file cmain.c
#include "cppfile.hpp"
int main(int argc, char **argv)
{
int i = myfunction(argv[1]);
printf("%d\n", i);
return 0;
}
Compiling:
gcc -c cmain.c
g++ -fPIC -shared -o cppfile.so cppfile.cpp
You want something more like this (and here I will use a slightly more meaningful example):
C/C++ header - animal.h
#ifndef ANIMAL_H
#define ANIMAL_H
#ifdef __cplusplus
class Animal {
public:
Animal() : age(0), height(0) {}
Animal(int age, float height) : age(age), height(height) {}
virtual ~Animal() {}
int getAge();
void setAge(int new_age);
float getHeight();
void setHeight(float new_height);
private:
int age;
float height; // in metres!
};
#endif /* __cplusplus */
#ifdef __cplusplus
extern "C" {
#endif
struct animal; // a nice opaque type
struct animal *animal_create();
struct animal *animal_create_init(int age, float height);
void animal_destroy(struct animal *a);
void animal_setage(struct animal *a, int new_age);
void animal_setheight(struct animal *a, float new_height);
int animal_getage(struct animal *a);
float animal_getheight(struct animal *a);
#ifdef __cplusplus
}
#endif
#endif /* ANIMAL_H */
C++ animal implementation file - animal.cpp
#include "animal.h"
#define TO_CPP(a) (reinterpret_cast<Animal*>(a))
#define TO_C(a) (reinterpret_cast<animal*>(a))
void Animal::setAge(int new_age) { this->age = new_age; }
int Animal::getAge() { return this->age; }
void Animal::setHeight(float new_height) { this->height = new_height; }
float Animal::getHeight() { return this->height; }
animal *animal_create() {
animal *a = TO_C(new Animal);
return a;
}
animal *animal_create_init(int age, float height) {
animal *a = TO_C(new Animal(age, height));
return a;
}
void animal_destroy(animal *a) {
delete TO_CPP(a);
}
void animal_setage(animal *a, int new_age) {
TO_CPP(a)->setAge(new_age);
}
void animal_setheight(animal *a, float new_height) {
TO_CPP(a)->setHeight(new_height);
}
int animal_getage(animal *a) {
TO_CPP(a)->getAge();
}
float animal_getheight(animal *a) {
TO_CPP(a)->getHeight();
}
C client code - main.c
#include "animal.h"
#include <stdio.h>
int main()
{
// 6'0" 25yo (perhaps a human? :P)
struct animal *a = animal_create(25, 1.83);
animal_setage(a, 26); // birthday
printf("Age: %d\nHeight: %f", animal_getage(a), animal_getheight(a));
animal_destroy(a);
return 0;
}
C++ client code - main.cpp
#include "animal.h"
#include <iostream>
int main()
{
// 6'0" 25yo (perhaps a human? :P)
Animal* a = new Animal(25, 1.83);
a->setAge(26); // birthday
std::cout << "Age: " << a->getAge() << std::endl;
std::cout << "Height: " << a->getHeight();
delete a;
return 0;
}
So when you compile the library, you compile animal.cpp with a C++ compiler. You can then link to it with C code, and use the animal_xxx functions.
Note the use of struct animal and Animal. Animal is a normal C++ type. It's exactly what it looks like. struct animal, on the other hand, is an "opaque" type. That means that your C program can see it's there, and can have one, but it doesn't know what is inside it. All it knows is that it has a function that takes a struct animal*.
In a real library you will want to have customisation points for memory allocation. So assuming this is the library libjungle, you probably want at least jungle_setmalloc and jungle_setfree with sensible defaults. You can then set up the global new and delete in libjungle's C++ code to use these user-defined functions.
This is entirely possible. Here is how, quickly:
1.) You have a header.h with a C API that doesn't include any Cplusiness.
#ifndef MIXEDCCPP_H
#define MIXEDCCPP_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h> // Any C-compatible headers will go here.
// C API goes here. C Functions can't contain any CPPiness.
void myclass_setName( void *pClassObj, const char *pName, int nameLen );
#ifdef __cplusplus
}
#endif
#ifdef __cplusplus
// Stuff that is only compatible with CPP goes here
// __cplusplus section won't get processed while compiling C files.
#include <vector> // CPP headers.
class MyClass {
// Classes etc.
};
#endif // #ifdef __cplusplus
#endif // MIXEDCCPP_H
Then in the .cpp, you simply create some C-API functions that can even include CPP right in them:
#include "mixedccpp.h"
extern "C" {
// C API goes here. C Functions can't contain any CPPiness in their prototypes.
void myclass_setName( void *pClassObj, const char *pName, int nameLen )
{
// But CPP knowledge can go inside the function - no problem, since this is a CPP file.
MyClass *pMyClass = static_cast<MyClass *>(pClassObj);
pMyClass->setName( pName, nameLen );
}
} // #extern "C"
// CPP Stuff goes here... or vice-versa.
In your case, you don't actually need any CPP code declared in your header since you are calling external libraries. But you need to create C-compatible functions in your CPP file which can call out to CPP libraries. Use extern "C" for those functions that need to be called from C files, and then use C-structs instead of classes and, if classes are needed, use void * to point to them and then cast them back to their class from the C function any time you need to access them. A standard makefile should be able to compile this just fine, assuming it compiles .cpp files as .cpp and understands extern "C" {}
Your C code cannot use the C++ header <string>. You have to ensure that the functions in the C++ API that are to be called from C are declared extern "C" (as you have), and use only types recognized by a C compiler (as you have).
You also need to link with the C++ compiler if any of your code is in C++. You can do it otherwise if you're prepared to spend a lot of energy getting the loader options right, but it is far simpler just to use the C++ compiler:
gcc -c cmain.c
g++ -fPIC -shared -o cppfile.so cppfile.cpp
g++ -o cmain cmain.o cppfile.so
Of course, you need to:
Add #include <stdio.h> in cmain.c.
Use std::string S(filename); in cppfile.cpp.
Also, if the program is invoked without arguments, you get:
$ ./cmain
terminate called throwing an exceptionAbort trap: 6
$ ./cmain x3
0
$
You need to protect against misuse, even in test programs.

namespace and c++/c mixed header

The following code explains the situation I encountered:
#ifdef __cplusplus
namespace ns
{
class pod
{
const short foo;
const char bar;
public:
pod(short f,char b):foo(f),bar(b){}
char foobar();
};
}
#else
typedef struct pod pod;
#endif
#ifdef __cplusplus
extern "C"{
#endif
extern pod* pod_new(short f, char b);//BANG!!!
extern char pod_foobar(pod* p); //BANG!!!
#ifdef __cplusplus
}
#endif
I can't put the C linkage functions inside the namespace ns, or the C client won't find their definitions. When I pull them out of the namespace, the C++ definition won't compile either,all because of the pod solution, which I learned from C++ FAQ Lite,it's only a preprocessor trick. And sadly, this trick couldn't deal with namespaces.
What am I supposed to do? Should I throw out all type-safety, and replace pod* with void*, or is there a nicer solution for this kinda situation? Any advices? Please!
I'd just give two different declarations for the functions
for C++:
extern "C" ns::pod* pod_new(short f, char b);
extern "C" char pod_foobar(ns::pod* p);
and for C:
typedef struct pod pod;
extern pod* pod_new(short f, char b);
extern char pod_foobar(pod* p);
But if this doesn't satisfy you, for C++ you could also have a typedef
typedef ns::pod ns_pod;
for C
typedef struct ns_pod ns_pod;
and then have the same common function prototype.
extern ns_pod* pod_new(short f, char b);
extern char pod_foobar(ns_pod* p);
Edit: In C the struct pod or struct ns_pod is an incomplete type, so in C directly you could never do anything that uses the fields or asks for its size. The difference between a pointer to an incomplete type and a void* is that you can only assign such a struct pointer to another struct pointer of the same incomplete type.
typedef struct ns_pod2 pod2;
ns_pod* q = pod_new(...); // valid
ns_pod2* r = pod_new(...); // a constraint violation! (= compiler error)
The second one would need an explicit cast, if you want to insist. This is one of the reasons why casts are frowned upon by many C programmers.
I figured it out myself :) by checking the symbols of the obj files with nm.
It turns out that the C++ namespaces have no effects on functions with C linkage, therefore I can rewrite the code above like this:
#ifdef __cplusplus
namespace ns
{
class pod
{
const short foo;
const char bar;
public:
pod(short f,char b):foo(f),bar(b){}
char foobar();
};
}
#else
typedef struct pod pod;
#endif
#ifdef __cplusplus
namespace ns{
extern "C"{
#endif
pod* pod_new(short f, char b);
char pod_foobar(pod* p);
void pod_free(pod* p);
#ifdef __cplusplus
}
}
#endif