diff --git a/src/metkit/CMakeLists.txt b/src/metkit/CMakeLists.txt index 49a3426b..5303e8b5 100644 --- a/src/metkit/CMakeLists.txt +++ b/src/metkit/CMakeLists.txt @@ -4,7 +4,6 @@ ecbuild_generate_config_headers( DESTINATION ${INSTALL_INCLUDE_DIR}/metkit ) configure_file( metkit_config.h.in metkit_config.h ) configure_file( metkit_version.h.in metkit_version.h ) -configure_file( metkit_version.cc.in metkit_version.cc ) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/metkit_config.h @@ -15,7 +14,6 @@ install(FILES ### metkit sources list( APPEND metkit_srcs - ${CMAKE_CURRENT_BINARY_DIR}/metkit_version.cc config/LibMetkit.cc config/LibMetkit.h mars/BaseProtocol.cc @@ -100,6 +98,8 @@ list( APPEND metkit_srcs hypercube/HyperCube.cc hypercube/HyperCube.h hypercube/HyperCubePayloaded.h + api/metkit_c.cc + api/metkit_c.h ) list( APPEND metkit_persistent_srcs diff --git a/src/metkit/api/metkit_c.cc b/src/metkit/api/metkit_c.cc new file mode 100644 index 00000000..09d4168d --- /dev/null +++ b/src/metkit/api/metkit_c.cc @@ -0,0 +1,323 @@ +#include "metkit_c.h" +#include "metkit/mars/MarsExpension.h" +#include "metkit/mars/MarsRequest.h" +#include "metkit/metkit_version.h" + +#include "eckit/exception/Exceptions.h" +#include "eckit/runtime/Main.h" +#include "eckit/utils/Optional.h" + +#include + +extern "C" { + +// --------------------------------------------------------------------------------------------------------------------- + +struct metkit_request_t : public metkit::mars::MarsRequest { + using metkit::mars::MarsRequest::MarsRequest; + metkit_request_t(const metkit::mars::MarsRequest& k) : + metkit::mars::MarsRequest(k) {} +}; + +struct metkit_requestiterator_t { + metkit_requestiterator_t(std::vector vec) : + first(true), vector(std::move(vec)), iterator(vector.begin()) {} + + int next() { + if (first) { + first = false; + } + else { + ++iterator; + } + if (iterator == vector.end()) { + return METKIT_ITERATION_COMPLETE; + } + return METKIT_SUCCESS; + } + + bool first; + std::vector vector; + std::vector::const_iterator iterator; +}; + +struct metkit_paramiterator_t { + metkit_paramiterator_t(std::vector vec) : + first(true), vector(std::move(vec)), iterator(vector.begin()) {} + + int next() { + if (first) { + first = false; + } + else { + ++iterator; + } + if (iterator == vector.end()) { + return METKIT_ITERATION_COMPLETE; + } + return METKIT_SUCCESS; + } + + bool first; + std::vector vector; + std::vector::const_iterator iterator; +}; + +// --------------------------------------------------------------------------------------------------------------------- +// ERROR HANDLING + +} // extern "C" + +static thread_local std::string g_current_error_string; + +const char* metkit_get_error_string(int) { + return g_current_error_string.c_str(); +} + +int innerWrapFn(std::function f) { + return f(); +} + +int innerWrapFn(std::function f) { + f(); + return METKIT_SUCCESS; +} + +template +[[nodiscard]] int tryCatch(FN&& fn) { + try { + return innerWrapFn(fn); + } + catch (const eckit::UserError& e) { + eckit::Log::error() << "User Error: " << e.what() << std::endl; + g_current_error_string = e.what(); + return METKIT_ERROR_USER; + } + catch (const eckit::AssertionFailed& e) { + eckit::Log::error() << "Assertion Failed: " << e.what() << std::endl; + g_current_error_string = e.what(); + return METKIT_ERROR_ASSERT; + } + catch (const eckit::Exception& e) { + eckit::Log::error() << "METKIT Error: " << e.what() << std::endl; + g_current_error_string = e.what(); + return METKIT_ERROR; + } + catch (const std::exception& e) { + eckit::Log::error() << "Unknown Error: " << e.what() << std::endl; + g_current_error_string = e.what(); + return METKIT_ERROR_UNKNOWN; + } + catch (...) { + eckit::Log::error() << "Unknown Error!" << std::endl; + g_current_error_string = ""; + return METKIT_ERROR_UNKNOWN; + } +} + +extern "C" { + +// ----------------------------------------------------------------------------- +// HELPERS +// ----------------------------------------------------------------------------- + +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; +} + +int metkit_initialise() { + return tryCatch([] { + static bool initialised = false; + + if (initialised) { + eckit::Log::warning() + << "Initialising Metkit library twice" << std::endl; + } + + if (!initialised) { + const char* argv[2] = {"metkit-api", 0}; + eckit::Main::initialise(1, const_cast(argv)); + initialised = true; + } + }); +} + +// ----------------------------------------------------------------------------- +// PARSING +// ----------------------------------------------------------------------------- + +int metkit_parse_mars_request(const char* str, metkit_requestiterator_t** requests, bool strict) { + return tryCatch([requests, str, strict] { + ASSERT(requests); + ASSERT(str); + std::istringstream in(str); + *requests = new metkit_requestiterator_t(metkit::mars::MarsRequest::parse(in, strict)); + }); +} + +// ----------------------------------------------------------------------------- +// REQUEST +// ----------------------------------------------------------------------------- + +int metkit_new_request(metkit_request_t** request) { + return tryCatch([request] { + ASSERT(request); + *request = new metkit_request_t(); + }); +} + +int metkit_free_request(const metkit_request_t* request) { + return tryCatch([request] { + ASSERT(request); + delete request; + }); +} + +int metkit_request_add(metkit_request_t* request, const char* param, const char* values[], int numValues) { + return tryCatch([request, param, values, numValues] { + ASSERT(request); + ASSERT(param); + ASSERT(values); + std::string n(param); + std::vector vv; + for (int i = 0; i < numValues; i++) { + vv.push_back(std::string(values[i])); + } + request->values(n, vv); + }); +} + +int metkit_request_set_verb(metkit_request_t* request, const char* verb) { + return tryCatch([request, verb] { + ASSERT(request); + ASSERT(verb); + request->verb(verb); + }); +} + +int metkit_request_verb(const metkit_request_t* request, const char** verb) { + return tryCatch([request, verb] { + ASSERT(request); + ASSERT(verb); + *verb = request->verb().c_str(); + }); +} + +int metkit_request_has_param(const metkit_request_t* request, const char* param, bool* has) { + return tryCatch([request, param, has] { + ASSERT(request); + ASSERT(param); + ASSERT(has); + *has = request->has(param); + }); +} + +int metkit_request_params(const metkit_request_t* request, metkit_paramiterator_t** params) { + return tryCatch([request, params] { + ASSERT(request); + ASSERT(params); + *params = new metkit_paramiterator_t(request->params()); + }); +} + +int metkit_request_count_values(const metkit_request_t* request, const char* param, size_t* count) { + return tryCatch([request, param, count] { + ASSERT(request); + ASSERT(param); + ASSERT(count); + *count = request->countValues(param); + }); +} + +int metkit_request_value(const metkit_request_t* request, const char* param, int index, const char** value) { + return tryCatch([request, param, index, value] { + ASSERT(request); + ASSERT(param); + ASSERT(value); + *value = (request->values(param, false))[index].c_str(); + }); +} + +int metkit_request_expand(const metkit_request_t* request, metkit_request_t* expandedRequest, bool inherit, bool strict) { + return tryCatch([request, expandedRequest, inherit, strict] { + ASSERT(request); + ASSERT(expandedRequest); + ASSERT(expandedRequest->empty()); + metkit::mars::MarsExpension expand(inherit, strict); + *expandedRequest = std::move(expand.expand(*request)); + }); +} + +int metkit_request_merge(metkit_request_t* request, const metkit_request_t* otherRequest) { + return tryCatch([request, otherRequest] { + ASSERT(request); + ASSERT(otherRequest); + request->merge(*otherRequest); + }); +} + +// ----------------------------------------------------------------------------- +// REQUEST ITERATOR +// ----------------------------------------------------------------------------- + +int metkit_free_requestiterator(const metkit_requestiterator_t* list) { + return tryCatch([list] { + ASSERT(list); + delete list; + }); +} + +int metkit_requestiterator_next(metkit_requestiterator_t* list) { + return tryCatch(std::function{[list] { + ASSERT(list); + return list->next(); + }}); +} + +int metkit_requestiterator_request(const metkit_requestiterator_t* list, metkit_request_t* request) { + return tryCatch([list, request] { + ASSERT(list); + ASSERT(request); + ASSERT(list->iterator != list->vector.end()); + ASSERT(request->empty()); + *request = std::move(*(list->iterator)); + }); +} + +// ----------------------------------------------------------------------------- +// PARAM ITERATOR +// ----------------------------------------------------------------------------- + +int metkit_free_paramiterator(const metkit_paramiterator_t* list) { + return tryCatch([list] { + ASSERT(list); + delete list; + }); +} + +int metkit_paramiterator_next(metkit_paramiterator_t* list) { + return tryCatch(std::function{[list] { + ASSERT(list); + return list->next(); + }}); +} + +int metkit_paramiterator_param(const metkit_paramiterator_t* list, const char** param) { + return tryCatch([list, param] { + ASSERT(list); + ASSERT(param); + ASSERT(list->iterator != list->vector.end()); + *param = list->iterator->c_str(); + }); +} + + +// --------------------------------------------------------------------------------------------------------------------- + +} // extern "C" \ No newline at end of file diff --git a/src/metkit/api/metkit_c.h b/src/metkit/api/metkit_c.h new file mode 100644 index 00000000..9bc7bbab --- /dev/null +++ b/src/metkit/api/metkit_c.h @@ -0,0 +1,221 @@ +#ifndef METKIT_API_METKIT_C_H +#define METKIT_API_METKIT_C_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* --------------------------------------------------------------------------------------------------------------------- + * TYPES + * -----*/ + +struct metkit_request_t; +typedef struct metkit_request_t metkit_request_t; + +struct metkit_requestiterator_t; +/** RequestIterator for iterating over vector of Request instances */ +typedef struct metkit_requestiterator_t metkit_requestiterator_t; + +struct metkit_paramiterator_t; +/** Iterator for iterating over parameters in Request */ +typedef struct metkit_paramiterator_t metkit_paramiterator_t; + +/* --------------------------------------------------------------------------------------------------------------------- + * ERROR HANDLING + * -------------- */ + +typedef enum metkit_error_values_t +{ + METKIT_SUCCESS = 0, /* Operation succeded. */ + METKIT_ITERATION_COMPLETE = 1, /* All elements have been returned */ + METKIT_ERROR = 2, /* Operation failed. */ + METKIT_ERROR_UNKNOWN = 3, /* Failed with an unknown error. */ + METKIT_ERROR_USER = 4, /* Failed with an user error. */ + METKIT_ERROR_ASSERT = 5 /* Failed with an assert() */ +} metkit_error_enum_t; + +const char* metkit_get_error_string(int err); + +/* ----------------------------------------------------------------------------- + * HELPERS + * ------- */ + +/** + * @brief Set MetKit version. + * + * @param version Version string + * @return int Error code + */ +int metkit_version(const char** version); + +/** + * @brief Set MetKit git sha1 version. + * + * @param sha1 SHA1 version string + * @return int Error code + */ +int metkit_vcs_version(const char** sha1); + +/** + * @brief Initialise Main() context. + * + * @note This is ONLY required when Main() is NOT initialised, such as loading + * the MetKit as shared library in Python. + * @return int Error code + */ +int metkit_initialise(); + +/* --------------------------------------------------------------------------------------------------------------------- + * PARSING + * --- */ + +/** + * Parse MARS requests into RequestIterator of Request instances. Resulting RequestIterator + * must be deallocated with metkit_free_requestiterator + * @param str MARS requests + * @param requests Allocates RequestIterator object + * @return int Error code + */ +int metkit_parse_mars_request(const char* str, metkit_requestiterator_t** requests, bool strict = false); + +/* --------------------------------------------------------------------------------------------------------------------- + * REQUEST + * --- */ + +/** Allocates new Request object. Must be deallocated with mekit_free_request + * @param request new Request instance + * @return int Error code + */ +int metkit_new_request(metkit_request_t** request); + +/** Deallocates Request object and associated resources. + * @param request Request instance + * @return int Error code + */ +int metkit_free_request(const metkit_request_t* request); + +/** Add parameter and values to request + * @param request Request instance + * @param param parameter name + * @param values list of values for parameter + * @param numValues number of values + * @return int Error code + */ +int metkit_request_add(metkit_request_t* request, const char* param, const char* values[], int numValues); + +/** Set verb in Request object + * @param request Request instance + * @param verb verb to set + * @return int Error code + */ +int metkit_request_set_verb(metkit_request_t* request, const char* verb); + +/** Returns the verb in Request object + * @param request Request instance + * @param verb verb in request + * @return int Error code + */ +int metkit_request_verb(const metkit_request_t* request, const char** verb); + +/** Returns whether parameter is in Request object + * @param request Request instance + * @param param parameter name + * @param has whether parameter exists in request + * @return int Error code + */ +int metkit_request_has_param(const metkit_request_t* request, const char* param, bool* has); + +/** Returns ParamIterator of parameters in request. Resulting ParamIterator + * must be deallocated with metkit_free_paramiterator + * @param request Request instance + * @param params Allocates ParamIterator object for parameter names in request + * @return int Error code + */ +int metkit_request_params(const metkit_request_t* request, metkit_paramiterator_t** params); + +/** Returns number of values for specific parameter in Request object + * @param request Request instance + * @param param parameter name in request + * @param count number of values for param in request + * @return int Error code + */ +int metkit_request_count_values(const metkit_request_t* request, const char* param, size_t* count); + +/** Returns value for specific parameter and index in Request object + * @param request Request instance + * @param param parameter name in request + * @param index index of value to retrieve for param in request + * @param value retrieved value + * @return int Error code + */ +int metkit_request_value(const metkit_request_t* request, const char* param, int index, const char** value); + +/** Populates empty Request object by expanding existing request + * @param request Request instance to be expanded + * @param expandedRequest empty Request instance to be populated + * @param inherit if true, populate expanded request with default values + * @param strict it true, raise error rather than warning on invalid values + * @return int Error code + */ +int metkit_request_expand(const metkit_request_t* request, metkit_request_t* expandedRequest, bool inherit = true, bool strict = false); + +/** Merges other Request object into existing request + * @param request Request instance to contain result of merge + * @param otherRequest other Request instance to merge + * @return int Error code + */ +int metkit_request_merge(metkit_request_t* request, const metkit_request_t* otherRequest); + +/* --------------------------------------------------------------------------------------------------------------------- + * REQUEST ITERATOR + * --- */ + +/** Deallocates RequestIterator object and associated resources. + * @param list RequestIterator instance + * @return int Error code + */ +int metkit_free_requestiterator(const metkit_requestiterator_t* list); + +/** Moves to the next Request element in RequestIterator + * @param list RequestIterator instance + * @return int Error code + */ +int metkit_requestiterator_next(metkit_requestiterator_t* list); + +/** Populates empty Requestion object with data from current element in RequestIterator + * @param list RequestIterator instance + * @param request empty Request instance to populate with data + * @return int Error code + */ +int metkit_requestiterator_request(const metkit_requestiterator_t* list, metkit_request_t* request); + +/* --------------------------------------------------------------------------------------------------------------------- + * PARAM ITERATOR + * --- */ + +/** Deallocates ParamIterator object and associated resources. + * @param list ParamIterator instance + * @return int Error code + */ +int metkit_free_paramiterator(const metkit_paramiterator_t* list); + +/** Moves to the next string element in ParamIterator + * @param list ParamIterator instance + * @return int Error code + */ +int metkit_paramiterator_next(metkit_paramiterator_t* list); + +/** Returns the current parameter name in ParamIterator + * @param list ParamIterator instance + * @param param current parameter name in list + * @return int Error code + */ +int metkit_paramiterator_param(const metkit_paramiterator_t* list, const char** param); + +#ifdef __cplusplus +} +#endif + +#endif /* METKIT_API_METKIT_C_H */ \ No newline at end of file diff --git a/src/metkit/metkit_version.cc.in b/src/metkit/metkit_version.cc.in deleted file mode 100644 index 60a39ecc..00000000 --- a/src/metkit/metkit_version.cc.in +++ /dev/null @@ -1,20 +0,0 @@ -#include "metkit/metkit_version.h" - -#ifdef __cplusplus -extern "C" { -#endif - -const char * metkit_version() { return metkit_VERSION; } - -const char * metkit_version_str() { return metkit_VERSION_STR; } - -unsigned int metkit_version_int() -{ - return 10000*metkit_VERSION_MAJOR + 100*metkit_VERSION_MINOR + 1*metkit_VERSION_PATCH; -} - -const char * metkit_git_sha1() { return "@metkit_GIT_SHA1@"; } - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/src/metkit/metkit_version.h.in b/src/metkit/metkit_version.h.in index 5f42d64b..658a5be7 100644 --- a/src/metkit/metkit_version.h.in +++ b/src/metkit/metkit_version.h.in @@ -8,20 +8,14 @@ #define metkit_VERSION_MINOR @metkit_VERSION_MINOR@ #define metkit_VERSION_PATCH @metkit_VERSION_PATCH@ -#ifdef __cplusplus -extern "C" { -#endif +inline const char * metkit_version() { return metkit_VERSION; } -const char * metkit_version(); - -unsigned int metkit_version_int(); - -const char * metkit_version_str(); +inline unsigned int metkit_version_int() { + return 10000*metkit_VERSION_MAJOR + 100*metkit_VERSION_MINOR + 1*metkit_VERSION_PATCH; +} -const char * metkit_git_sha1(); +inline const char * metkit_version_str() { return metkit_VERSION_STR; } -#ifdef __cplusplus -} -#endif +inline const char * metkit_git_sha1() { return "@metkit_GIT_SHA1@"; } #endif // metkit_version_h