简体   繁体   中英

How to access an std::string member of C++ struct in C code

Suppose I have mixed C++ and C code. Inside the C++ code, I have a struct in my_cpp_code.h :

typedef struct {
    std::string some_string;
    int some_int;
    bool some_bool;
} CppStruct;

Having another source file, say C my_c_code.c . Is it possible to access the some_string member of the struct in this file as a const char * ?

AC compiler wouldn't understand the definition of CppStruct . So no, it is not possible for a C program to access CppStruct::some_string . But you can provide a set of C++ functions exposing a C-compatible signature to read/write from/to CppStruct::some_string .

// === compat.h ===
struct CppStruct;
#ifdef __cplusplus
extern "C" {
#endif
    CppStruct* get_the_damn_struct();
    int set_some_string(CppStruct*, char* data, size_t len);
    int get_some_string(CppStruct*, char* data, size_t* len);
#ifdef __cplusplus
}
#endif

// === compat.cpp ===
// Implementation

// === c_program.c ===
#include <compat.h>

// usage (e.g.:
CppStruct* wrapper = get_the_damn_struct();
char* data = "Hello, World!";
if (0 != set_some_string(wrapper, data, strlen(data)) {
    // error handling
}
//)

Not directly no.

One solution would be to have an opaque pointer on the C language side (a bit like FILE* ), that you pass to a function which is implemented on the C++ side (and is compiled with C linkage: consult your compiler documentation on how to do that). That function can have a const char* return type since both C and C++ understand that. That function can then reintepret_cast that opaque pointer ( p , say) to an instance of your struct , then return p->some_string.c_str() back.

You can have one C++ function that returns a pointer to such a struct-object, yet as void* , and you can have another C++ function that takes such a void* -pointer to a struct object and - after having casted it back - accesses the member's c_str -function. Declare these functions a pure C-functions in a separate header file (not mixed with C++ code):

Example:

// my_cpp_bridge.h:
void* getAStructPtr();
const char* getStringMemberPtr(void* structPtr);

// my_cpp_bridge.cpp:
#include "my_cpp_code.h"
#include "my_cpp_bridge.h"
void* getAStructPtr() {
   static CppStruct someSample;
   someSample.some_string = "Hello world!";
   return (void*)&someSample;
}
const char* getStringMemberPtr(void* structPtr) {
   const CppStruct *structPtr = reinterpret_cast<CppStruct*>(structPtr);
   return structPtr->c_str();
}

// my_test.c:
#include "my_cpp_bridge.h"
int main() {
   void* s = getAStructPtr();
   const char* content = s->getStringMemberPtr();
}

Your question might be an "XY problem" (you ask question 'X' but really you'd do better to ask 'Y' if only you knew it might be a better option). This answer poses an alternative that technically doesn't answer the question that you asked, but it can solve the problem that prompted the question:

Compile your legacy C code as C++ and this isn't even an issue.

If you must keep your C code pure C, then any of the other answers are what you're looking for. But if, like me, you're working on a project that uses legacy C code mixed with new C++ code, then perhaps you should start moving the legacy code away from pure C. Of course if you need to keep the legacy code "pure C" because it's shared with a separate project that's still compiled as strict C then this isn't an option, but let's suppose that's not a requirement. I've done this twice now on two large firmware projects with excellent results.

If you compile your C code as C++, then you can start to take advantage of C++ features even in your legacy "C" code. One way to do that is to find a compiler option to treat C as C++ code; eg Visual Studio's /TP option (see this SO post for more info). For GCC, change your makefile to use g++. Or, if there is no such option, rename your .c files with .cpp extensions.

Once you're compiling C code as C++, you may run into other minor problems, but those shouldn't be too difficult to work through, and the long-term benefits will outweigh the initial hassle. See this SO post for more information about that. Perhaps the main issue is that if you use malloc(), you will need to typecast the return value before assigning it to a typed pointer.

Once you're compiling your legacy code as C++, then you can do everything in your legacy code that you can do in your new C++ code, so the issue posed by your original question is no longer an issue at all. You #include <string> in your C file and then just call myCppStruct.some_string.c_str() any time you need a raw const char* pointer (eg to call other legacy C functions that take const char* parameters). No need for a utility function to do it for you.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM