tags:

views:

210

answers:

6

So it's been a while since I've used straight C. And I'm on a project where I'm working on an API in C++. Most of these methods are just C anyway, and all of the return values are C structures. Except one. One method I need to return a vector<string>. Now here's my question. Is C++ methods/libraries/whatever callable from C? I ask because I don't know if the people using the API are going to be writing in C or C++, and I feel like I should be returning only C structures. That would require me to return a char**, right?

I hope that made sense, if not:

tl;dr version - Can I call a C++ method from C if it returns a C structure, and if so is the best (only?) equivalent return value of vector<string> -> char**?

Update: The C++ methods are simply global methods. There's no classes or object oriented stuff in them. The ONLY thing that's specific to C++ other than my vector question is a few stringstreams

+5  A: 

See this FAQ. Basically, you can't call C++ methods (member functions), but you can call free-standing functions if they're declared with extern C. char ** is not the only possibility, but it's probably the most straight-forward. You can return a dynamically allocated array of char *. You will have to use an out parameter to provide the length to the caller (you could NULL-terminate it, but that's probably not ideal). E.g.

char **get_string_list(size_t *len)
{
  char **array;
  size_t actual_len;
  // ...
  *len = actual_len;
  array = (char **) malloc(sizeof(char *) * actual_len);
  // ...
  return array;
}

You must somehow free the memory. Either provide a function or document how the caller should do it. Don't forget to free the individual strings if they're dynamically allocated.

Matthew Flaschen
I guess on a related note, can you free memory in C that was allocated with `new` in C++?
Falmarri
No. Simple cases might work coincidentally on some compilers (since `new` is often implemented with `malloc`), but it's definitely wrong.
Matthew Flaschen
+6  A: 

No, C cannot use C++ features that are not also available in C. However, C code can make use of C++ code indirectly. For example, you can implement a C function using C++, and you can use opaque types in the interface so that the signature uses void*, but the implementation uses a C++ class.

The equivalent of vector<string> in C is probably closer to:

 typedef const char* c_string_type;
 typedef struct c_string_array {
     c_string_type* c_strings;
     int c_strings_count;
  } c_string_array_t;

With opaque types, you would have something along the lines of:

 typedef void* c_string_array_t;
 int c_string_array_length(c_string_array_t array);
 const char* c_string_array_get(c_string_array_t array, int index);

You could then secretly (in the C++ implementation) cast std::vector* to void*.

Michael Aaron Safyan
Why not use a struct to hold the length of the string, as well? It'll save the C user strlenning every string when the length is already known.
DeadMG
@DeadMG: Don't presume to know what the user will do with his data. You may be incurring performance penalty for no reason. They might not even be textual strings.
Matt Joiner
@Michael: Don't use _t, this reserved by POSIX.
Matt Joiner
@Matt Joiner: If you have a pointer to an array, it's useless without the size, whatever's in it.
DeadMG
`char *blah[] = {"are", "you", "sure?", NULL}; return blah;`
Matt Joiner
All good answers. Upvoted accordingly
Falmarri
@Matt, I am aware of that. However, in practice, as long as you prefix your types with the name of your library (e.g. "mylibrary_cstring_type_t") then you aren't going to get a name clash.
Michael Aaron Safyan
A: 

I read some lines about this some time ago, and iirc you can use c++ code/structures that would compile in C. But there is some thing about static initialisation, if i understood correct, you should write your main function in c++ to guarantee that this s.i. is done, C main does not do.

InsertNickHere
We aren't writing anything in main, or anything like that. We're writing an API to interface with our socket server
Falmarri
+6  A: 

You can technically call anything from C, as long as a C-visible function name is given (prototypes etc. are ignored at the ABI level). Of course you can't expect correct results if C isn't able to generate the parameters in the expected fashion. Generally the obvious solution is to simplify the interface down to the C level. char ** is an excellent choice for greatest common denominator with a vector<string>. Not only that, if you know what you intend to do with it, quite possibly faster (and cleaner IMHO).

With respect to the C visibility: The function name cannot be shared with any other C visible functions. If you wish your C++ function to be callable from C, this might be a good example of the prototype:

extern "C" char **lots_of_strings();

If the parameter signature differs, C++ will let you overload with functions visible only from C++, and allow them to coexist with the C version:

vector<string> lots_of_strings(int);
extern "C" char **lots_of_strings();

If you wanted to provide several ways to call it, appropriate for the calling language, you might try this (ignoring the evils of late initialization, and the fact that bool exists in C):

bool lots_of_strings(vector<string> &);
extern "C" int lots_of_strings(char ***);
Whatever lots_of_strings(SomeArrayType &);

Keeping in mind that in every case, C++ will choose the definition with the best matching signature to the call site, and C will take whatever it can (which is always a single function with a matching name).

You'll find it useful to hide C++isms from C by combining #ifdef with the macro __cplusplus.

Matt Joiner
+1 for offering different interfaces to C and C++, even if the sample interfaces would not be my first choice.
David Rodríguez - dribeas
I'm worried what your first choice would be then. Given the OPs requirements, anything more than a `vector<string>` or `char **` are overkill _immediately_ overkill.
Matt Joiner
A: 

If you intend to offer the same functionality to both C and C++ I would try to offer two entry points so that you can adapt one to the other. Note that while you can settle for an interface that can be used both from C and C++, in most cases the C interface will not be ideal for C++ usage (using char** might be a good solution for C, but having users convert that back to C++ types and performing cleanup can clutter user code) and vice versa.

David Rodríguez - dribeas
A: 

If you have the possibility to modify the code that you're calling I would suggest changing the function from returning an vector to something like:


unsigned int MyFunction(char* buff, unsigned int numLines, unsigned int stride)

Where numLines is the amount of allocated strings and stride is the size of the strings. That way your function doesn't need to allocate any memory that you need to worry about later. It's all handled by the caller. The return value of the function is the amount of strings that was used.

Simon