-1

I'm implementing a C API that exposes some data collected from a systemd service. The header of the API contains two structs:

typedef struct Info {
  int latest;
  int prev;
  int cur;
} Info;

typedef struct InfoArray {
  Info *info;
} InfoArray;

The implementation of this C header is written in C++ and I plan to offer a create(size_t size) function to let the user create a new struct with an array of the user entered size. This returned array can then later be passed by reference to another function to fill it with data until the array has reached the size defined by the user.

How would I achieve this?

Edit: I'm looking for a way to return the allocated array of size n, however I tried the following:

InfoArray *create(size_t size) {
  InfoArray *array = (InfoArray *)malloc(size * sizeof(Info));
  return array;
}

which should return a allocated array, but when trying to delete the array on the client side by calling delete info I get an error that I cannot free this handle, so how would i let the consumer of the API manage the deletion of the memory?

Since the implementation is written in a .cpp file (C++) i figured there must be a way to return it by using the new operator, but I've failed on implementing it like this.

Xershy
  • 177
  • 11
  • 4
    What have you tried and where is your problem? – UnholySheep Oct 12 '22 at 10:07
  • 2
    Writing a function that creates a simple object like that is relatively simple, so we need to know where you got stuck. As written, the question seems like you're asking for other people to do all the work for you, which is not acceptable. – Thomas Jager Oct 12 '22 at 10:15
  • Something like `Info *create(size_t size) {Info *newinfo;....; return newinfo;}`? Where do you have difficulties? – Jabberwocky Oct 12 '22 at 10:16
  • Please explain what info you need beyond https://stackoverflow.com/questions/17590226/finding-length-of-array-inside-a-function otherwise I will close this as a duplicate of that. Especially clarify whether you really can accept C++ solutions for your C question, because there it is easier and different. If you cannot please remove the C++ tag (it will also save you from comments that C is not C++). (Everybody without the hammer, please propose duplicate if you know one.) – Yunnosch Oct 12 '22 at 10:17
  • added an edit in my post for clarification @Yunnosch – Xershy Oct 12 '22 at 10:47
  • Are you really trying to use the C++ `delete` on something created by C `malloc()`? I think you need to redesign your interface. – Yunnosch Oct 12 '22 at 10:57
  • You're not initialising Info* info, you don't need to create a separate object InfoArray. – Ieshaan Saxena Oct 12 '22 at 11:01
  • @Yunnosch how would I implement the create function using the C++ equivalent of malloc() that's what I'm after it seems. – Xershy Oct 12 '22 at 11:01
  • You cannot use the C++ version of malloc in something you want to write in C or which has a C interface. – Yunnosch Oct 12 '22 at 11:26
  • @Yunnosch There is no "C++ version of malloc" just like there is no "C++ version of `int`". – n. m. could be an AI Oct 12 '22 at 11:39
  • @n.1.8e9-where's-my-sharem. Yes exactly, you seem to support my point. I think I miss your point. – Yunnosch Oct 12 '22 at 11:42
  • You said something about "C++ version of malloc" that cannot be used in some place or another. Well a thing that does not exist cannot be used, that's true. You can use `malloc` freely in a mixed C/C++ program, there is only one `malloc` between C and C++. – n. m. could be an AI Oct 12 '22 at 11:46
  • The API does not make much sense. `struct InfoArray` presumably contains or points to or somehow refers to an array of `Info`. This is corroborated by having `Info *info;` inside. It however does not expose the size of the array, making it unusable in practice. Then you are making a separate array of `struct InfoArray` (each presumably pointing to an array if `struct Info`) without initialising the `info` filed in any of them. How do you plan to use this thing? – n. m. could be an AI Oct 12 '22 at 11:55
  • @n.1.8e9-where's-my-sharem. Please see my comments in context of OPs comments and OPs edit on the question. I think you will see that you and I agree. – Yunnosch Oct 12 '22 at 13:02
  • Strictly speaking, it would be imaginable to use something with a C interface from C++ and to even implement it in C++ with a C interface. I just really hope it is not what OP wants. And whatever comes out of a C interface will never be accepted by `delete`, the C++ code which uses the C interface has to accept that it is a C interface. That is why I hinted to redesign the interface. Either drop the C interface between two pieces of C++. Or go with the C interface and drop the idea to use `delete`. – Yunnosch Oct 12 '22 at 13:51
  • To clarify a few things: We actually use a C header to define the Interface and internally use C++ to implement said interface, this is not the problem and will work when properly returning the structures defined in the C header. I was only looking for a 'clean' way to allocate an array of a struct in the library and pass that to the consumer. I've indeed interchanged the delete() and free() function - meant to use ```free(obj)```. – Xershy Oct 12 '22 at 14:06

3 Answers3

1

When using malloc(), to clear the object you have to use free(). This is the C way of doing things.

When using C++, use new and delete instead, like this: InfoArray *array = new InfoArray[size];

However, when going with C++ i would urge you to just dump the InfoArray thing and roll with something like std::vector<Info>.

Edit:

The implementation of this C header is written in C++

This doesn't make sense. It's either a C unit or a C++ unit. Those are two separate languages, you only get to pick one. Similiar syntax and good interfaceability do not change that.

nick
  • 541
  • 1
  • 9
  • I think you basically are on the right track. I worry however that OP wants to create something in C and with C interface and then use C++ `delete` on it. I really hope I misunderstood... – Yunnosch Oct 12 '22 at 13:04
  • @Yunnosch: Yeah, there's certainly some confusion going on. What do you think of my edit ? – nick Oct 12 '22 at 13:34
  • 1
    I still am unhappy, but more with the question than with your answer. I wish OP would answer to clarification questions... – Yunnosch Oct 12 '22 at 13:52
  • 1
    I answered by a different angle. I do not see a conflict between our answers. Together they probably give OP what they need, maybe what they want. – Yunnosch Oct 12 '22 at 13:59
1

I see "I'm implementing a C API", so the interface is C, I understand.
I see "implementation of this C header is written in C++" so the code behind the interface is C++ by your design.
I see "trying to delete the array on the client side by calling delete info", so the calling code is also C++.

Strictly speaking, it would be imaginable to use something with a C interface from C++ and to even implement it in C++ with a C interface. I just really hope it is not what OP wants.
But whatever comes out of a C interface will never be accepted by delete, the C++ code which uses the C interface has to accept that it is a C interface and use free() on the malloced pointer.
That is why I recommend to redesign the interface. Either drop the C interface between two pieces of C++ in favor of a C++ interface. Or go with the C interface and drop the idea to use delete.

Yunnosch
  • 26,130
  • 9
  • 42
  • 54
1

how would i let the consumer of the API manage the deletion of the memory?

Problems with freeing is one of the reasons why C idiom is for the caller to provide a buffer:

int fillInfo(InfoArray *array, size_t arraySize) {
  memcopy...
  return countCopied;
}

In this approach the API never allocates anything, it only copies the data to provided buffers. Just like strcpy. The huge upside of this approach is that client can use whatever allocation methods they deem appropriate at the moment.

If you really must return a new buffer then provide a pair of methods:

InfoArray *create(size_t size);
void release(array* InfoArray);

This way clients don't know nor care how the objects are allocated and freed. This is useful when you want to restrict the client how the memory is allocated, but note that the client is still responsible for memory management, it's just limited to one option. And it still doesn't guarantee the client won't use memory allocated in unapproved ways.

Keeping allocation and freeing on 2 sides of the demarcation line between client and library is asking for trouble. You're hiding from the client author how the memory was allocated, and yet you require them to use correct deallocation method. Which is precisely the culprit here.

Agent_L
  • 4,960
  • 28
  • 30