Reputation: 3486
I'm working on a wrapper for a C++ library that I need to use with plain C. The library has a function called checkError()
. This function throws an exception related to the error which has occurred within the object. In the C++ wrapper, I'm catching this error, mallocing a C-compatible structure, assigning the error message and information to the error structure variable, and then I'm passing back the pointer to the structure. This is all fine and dandy, but I'm worried about memory leaks once the C calling function has processed the structure.
For this issue, let's assume the library's object is called objectA
and the library is called libraryA
Here's basically what I have:
The C & C++ compatible wrapper.h
:
#ifndef LIBRARY_WRAPPER_DEFINITION
#define LIBRARY_WRAPPER_DEFINITION
#ifdef __cplusplus
extern "C" {
#endif
typedef struct wrapper_error_message_struct{
char *what;
char *typeAsText;
unsigned char severityLevel; /* 0-10; 0 = lowest severity. 10 = highest severity */
} wrapper_error_message_t;
typedef void *pLibObj_t;
/**
* I've omitted the other prototypes, typedefs, etc.
**/
/* checkError()'s wrapper prototype */
wrapper_error_message_t *wrapper_check_error(pLibObj_t ptrObjectToCheck);
#ifdef __cplusplus
}
#endif
#endif
The C++ implementation of wrapper_check_error
within wrapper.cpp
:
/* Imports and other functions preceding wrapper_check_error() */
wrapper_error_message_t *wrapper_check_error(pLibObj_t ptrObjectToCheck){
/* for the sake of this question, I've omitted the validity checks on
* ptrObjectToCheck. I've also omitted my malloc checks. */
libraryA::objectA *convertedObj = (libraryA::objectA *)ptrObjectToCheck;
size_t structSize = sizeof(wrapper_error_message_t);
size_t charSize = sizeof(char); // just in case it's not 1 on this system.
try{
convertedObj->checkError();
/* if the checkError() function did not throw an error, then we
can simply return NULL indicating that no ERROR was thrown */
return NULL;
}catch(libraryA::SomeLibraryExceptionType_1 &err){
wrapper_error_message_t *cErr = (wrapper_error_message_t *)malloc(structSize);
cErr->severityLevel = 3;
const char *errWhat = err.what();
cErr->what = (char *)malloc((strlen(errWhat)+1) * charSize);
strcpy(cErr->what, errWhat);
const char errorType[] = "Library Exception Type Name 1";
cErr->typeAsText = (char *)malloc((strlen(errorType)+1) * charSize);
strcpy(cErr->typeAsText, errorType);
return cErr;
}catch(libraryA::SomeLibraryExceptionType_2 &err){
/* Roughly the same as SomeLibraryExceptionType_1's catch statement */
}catch(libraryA::SomeLibraryExceptionType_3 &err){
/* Roughly the same as SomeLibraryExceptionType_1's catch statement */
}catch(std::exception &err)
wrapper_error_message_t *cErr = (wrapper_error_message_t *)malloc(structSize);
cErr->severityLevel = 7;
const char *errWhat = err.what();
cErr->what = (char *)malloc((strlen(errWhat)+1) * charSize);
strcpy(cErr->what, errWhat);
const char errorType[] = "Unknown standard exception (std::exception)";
cErr->typeAsText = (char *)malloc((strlen(errorType)+1) * charSize);
strcpy(cErr->typeAsText, errorType);
return cErr;
}catch(...){
wrapper_error_message_t *cErr = (wrapper_error_message_t *)malloc(structSize);
cErr->severityLevel = 10;
cErr->what = NULL;
const char errorType[] = "Unknown. Could not be caught.";
cErr->typeAsText = (char *)malloc((strlen(errorType)+1) * charSize);
strcpy(cErr->typeAsText, errorType);
return cErr;
}
}
The plain C function which is using wrapper_check_error()
in main.c
:
/* imports, irrelevant functions, irrelevant variable definitions */
void someFunction(){
pLibObj_t ourWrappedObj;
/*
* function code before error check.
*/
wrapper_error_message_t *errorMsg = wrapper_check_error(ourWrappedObj);
if(wrapper_error_message_t != NULL){
/* there was an error.
* the code within this if statement:
* - processes the error message
* - logs information about it (current time, type, severity and the what message)
* - makes logical decisions about how to handle it if possible.
*/
free(errorMsg);
errorMsg = NULL;
/* In the line above, the function frees the malloc'd structure to remove it
* from the heap.
*
* This free statement is what I'm concerned about.
*
*/
}
// etc.
}
Will free(errorMsg)
also free char *what
and char *typeAsText
because they're members of the structure which is being freed? Based on some of the reading I've done, I currently believe the values pointed to by what
and typeAsText
will still exist on the heap because errorMsg
only contains pointers to these values rather than the values themselves.
If *what
and *typeAsText
will still be on the heap, I'll write a function to free the members of the struct before freeing the struct itself. However, I only want to do that if it's necessary.
If someone can provide some guidance/insight about this, it would be greatly appreciated.
Thank you.
I apologize in advance if this question is a duplicate. If it is, please point me in the direction of the similar question so I can read the answers there. I've searched on SO as well as other sites, but I have not found anything that answers my question.
I took excerpts of code from my project, shortened the code, and renamed variables/functions. Aside from some error checking, if there is a blatant error in the code that I somehow didn't notice, please post a comment so I can revise it. If anything is unclear, let me know in the comments and I'll do my best to update the question and clarify.
Upvotes: 1
Views: 99
Reputation: 340188
Since there are three malloc()
calls used to build the object that you're passing ownership to:
wrapper_error_message_t *cErr = (wrapper_error_message_t *)malloc(structSize);
cErr->severityLevel = 3;
const char *errWhat = err.what();
cErr->what = (char *)malloc((strlen(errWhat)+1) * charSize);
strcpy(cErr->what, errWhat);
const char errorType[] = "Library Exception Type Name 1";
cErr->typeAsText = (char *)malloc((strlen(errorType)+1) * charSize);
strcpy(cErr->typeAsText, errorType);
return cErr;
there will need to be 3 calls to free()
:
free(errorMsg->typeAsText);
free(errorMsg->what);
free(errorMsg);
As a side note, sizeof(char)
is 1 by definition, so there's no need to do anything special for that.
And as another side note, I'd suggest using strdup()
instead of error-prone clutter like:
cErr->what = (char *)malloc((strlen(errWhat)+1) * charSize);
strcpy(cErr->what, errWhat);
Upvotes: 3