-
Notifications
You must be signed in to change notification settings - Fork 8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature/c api #57
base: develop
Are you sure you want to change the base?
Feature/c api #57
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## develop #57 +/- ##
===========================================
+ Coverage 60.39% 61.11% +0.71%
===========================================
Files 101 103 +2
Lines 6242 6517 +275
Branches 585 590 +5
===========================================
+ Hits 3770 3983 +213
- Misses 2472 2534 +62 ☔ View full report in Codecov by Sentry. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Todo: Ensure we cover all functionality currently in fdb_c so that we can replace it with this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good so far. The functionality of the pyfdb is the following:
struct fdb_request_t;
typedef struct fdb_request_t fdb_request_t;
int fdb_new_request(fdb_request_t** req);
int fdb_request_add(fdb_request_t* req, const char* param, const char* values[], int numValues);
int fdb_expand_request(fdb_request_t* req);
int fdb_delete_request(fdb_request_t* req);
Those are all contained in the request section. Do we need anything on top @ChrisspyB?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Like the changes. Code and Documentation was extraordinarily clean. I was a bit nitpicky. Bear with me in case some of my suggestions are not compatible with modern C; it has been a while since I programmed in C ;)
/// @comment: (maby) | ||
/// Not sure if there is much value in having a param iterator. We could just return an array of | ||
/// strings (char**) using metkit_marsrequest_params. | ||
/// I think we should metkit_paramiterator_t. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here the comment is lacking some words. Do this need to be addressed before merging?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a todo left for me by me
|
||
} // extern "C" | ||
|
||
static thread_local std::string g_current_error_string; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is read-only, isn't it? Make it const.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good so far. The functionality of the pyfdb is the following:
struct fdb_request_t;
typedef struct fdb_request_t fdb_request_t;
int fdb_new_request(fdb_request_t** req);
int fdb_request_add(fdb_request_t* req, const char* param, const char* values[], int numValues);
int fdb_expand_request(fdb_request_t* req);
int fdb_delete_request(fdb_request_t* req);
Those are all contained in the request section. Do we need anything on top @ChrisspyB?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You need an extra test where you use the API and compile the test with a C compiler
typedef enum metkit_error_values_t | ||
{ | ||
METKIT_SUCCESS = 0, /* Operation succeded. */ | ||
METKIT_ITERATION_COMPLETE = 1, /* All elements have been returned */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not an error and should be handled differently. Right now this mixes error and result states. I would advocate for a strict separation.
* | ||
* @note This is ONLY required when Main() is NOT initialised, such as loading | ||
* the MetKit as shared library in Python. | ||
* @return int Error code |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should document the possible error codes returned from this function and under which circumstances the errors are to be expected.
The underlying issue here is that every possible error that can happen when using this API is corralled into metkit_error_values_t
. When using such an API you will greatly thank the doc writer for stating the possible error cases, (think man pages for example) because this allows me to simplify error handling. And treat every unexpected EC as fatal error.
If the user is not given any information about the possible errors, he will always be wondering "Could this return a METKIT_ITERATION_COMPLETE?"
* @param it RequestIterator instance | ||
* @return int Error code | ||
*/ | ||
int metkit_requestiterator_next(metkit_requestiterator_t* it); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this would make for a nicer API if we could use it like this:
while((res = metkit_paramiterator_next(it))) {
//...
}
Ofc this would only work if the iteration cannot create errors. But return a nullptr on end of iteration makes for a more natural api imo.
/// Not sure if there is much value in having a param iterator. We could just return an array of | ||
/// strings (char**) using metkit_marsrequest_params. | ||
/// I think we should metkit_paramiterator_t. | ||
struct metkit_paramiterator_t { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lifetimes of returned values in this iterator differ from the metkit_requestiterator_t
iterator. For itself the lifetime is ok (if documented) but a subtle difference in lifetime handling with both types imply symmetrical behavior is a nasty trap. metkit_requestiterator_t
result lifetimes exceed the iterator due to the move out of the iterator, while the char*
from this iterator dangle as soon as the iterator is freed.
int metkit_version(const char** version) { | ||
*version = metkit_version_str(); | ||
return METKIT_SUCCESS; | ||
} | ||
|
||
int metkit_vcs_version(const char** sha1) { | ||
*sha1 = metkit_git_sha1(); | ||
return METKIT_SUCCESS; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So since its always METKIT_SUCCESS
this should just return the const char*
Maybe we should rewrite test_metkit_c.cc in C? For the time being I'll add a simple test that at least uses a C compiler |
Re: error handling, if we're going to do similar across the stack, it might make sense to centralise this somewhere (eckit). All of the caught exceptions are in fact eckit::exceptions |
# Compile C test | ||
ecbuild_add_test( TARGET metkit_test_c_compiled | ||
SOURCES test_c_api.c | ||
INCLUDES "${ECKIT_INCLUDE_DIRS}" | ||
LIBS metkit | ||
NO_AS_NEEDED | ||
ENVIRONMENT "${metkit_env}" | ||
LINKER_LANGUAGE C) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is not required to set LINKER_LANGUAGE C
as this will be detected based on the file extension. If you still want to be explicit its probably better to set the language for the source file to 'C' as we want the source file to pass trough the C-Compilation, i.e. set_source_files_properties(test_c_api.c PROPERTIES LANGUAGE C)
You should also probably also add C to the project languages. Right now C is always enabled due to how we pull in ecbuild and hence it works but it is surprising to see that we invoke the C-Compiler although the make project explicitly only requests C++.
I.e. the top-level project declaration should become project( metkit LANGUAGES CXX C)
No description provided.