Skip to content
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

Open
wants to merge 14 commits into
base: develop
Choose a base branch
from
Open
4 changes: 2 additions & 2 deletions src/metkit/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
348 changes: 348 additions & 0 deletions src/metkit/api/metkit_c.cc
Copy link
Member Author

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.

Copy link

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?

Original file line number Diff line number Diff line change
@@ -0,0 +1,348 @@
#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"
ChrisspyB marked this conversation as resolved.
Show resolved Hide resolved

#include <functional>

extern "C" {
ChrisspyB marked this conversation as resolved.
Show resolved Hide resolved

// ---------------------------------------------------------------------------------------------------------------------

struct metkit_marsrequest_t : public metkit::mars::MarsRequest {
using metkit::mars::MarsRequest::MarsRequest;

metkit_marsrequest_t(metkit::mars::MarsRequest&& k) :
ChrisspyB marked this conversation as resolved.
Show resolved Hide resolved
metkit::mars::MarsRequest(std::move(k)) {}
};

struct metkit_requestiterator_t {
metkit_requestiterator_t(std::vector<metkit::mars::MarsRequest>&& vec) :
ChrisspyB marked this conversation as resolved.
Show resolved Hide resolved
vector_(std::move(vec)), iterator_(vector_.begin()) {}

int next() {
if (iterator_ == vector_.end()) {
return METKIT_ITERATION_COMPLETE;
}
++iterator_;
return iterator_ == vector_.end() ? METKIT_ITERATION_COMPLETE : METKIT_SUCCESS;
}

void current(metkit_marsrequest_t* request) {
ASSERT(iterator_ != vector_.end());
*request = std::move(*iterator_);
}

private:

std::vector<metkit::mars::MarsRequest> vector_;
std::vector<metkit::mars::MarsRequest>::iterator iterator_;
};

/// @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.
Copy link

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?

Copy link
Member Author

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

struct metkit_paramiterator_t {
Copy link

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.

metkit_paramiterator_t(std::vector<std::string> vec) :
ChrisspyB marked this conversation as resolved.
Show resolved Hide resolved
vector_(std::move(vec)), iterator_(vector_.begin()) {}

int next() {
if (iterator_ == vector_.end()) {
return METKIT_ITERATION_COMPLETE;
}
++iterator_;

return iterator_ == vector_.end() ? METKIT_ITERATION_COMPLETE : METKIT_SUCCESS;
}

void current(const char** param) {
ASSERT(iterator_ != vector_.end());
*param = iterator_->c_str();
}

private:
std::vector<std::string> vector_;
std::vector<std::string>::iterator iterator_;
};

// ---------------------------------------------------------------------------------------------------------------------
// ERROR HANDLING

} // extern "C"

static thread_local std::string g_current_error_string;
Copy link

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.


const char* metkit_get_error_string(int) {
return g_current_error_string.c_str();
}
ChrisspyB marked this conversation as resolved.
Show resolved Hide resolved

int innerWrapFn(std::function<int()> f) {
return f();
}

int innerWrapFn(std::function<void()> f) {
f();
return METKIT_SUCCESS;
}

template <typename FN>
[[nodiscard]] int tryCatch(FN&& fn) {
try {
return innerWrapFn(fn);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably contain an std::forward?

}
catch (const eckit::UserError& e) {
eckit::Log::error() << "User Error: " << e.what() << std::endl;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Streams and the usage of std::endl can be problematic. In case of being unbuffered or line buffered this is fine. In case of a more sophisticated buffering strategy this can lead to additional effort.

g_current_error_string = e.what();
return METKIT_ERROR_USER;
}
catch (const eckit::AssertionFailed& e) {
eckit::Log::error() << "Assertion Failed: " << e.what() << std::endl;
ChrisspyB marked this conversation as resolved.
Show resolved Hide resolved
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 = "<unknown>";
ChrisspyB marked this conversation as resolved.
Show resolved Hide resolved
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;
}
Copy link

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*


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};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nullptr instead of 0. Why is this const if a line below the const-cast takes place?

eckit::Main::initialise(1, const_cast<char**>(argv));
initialised = true;
}
});
}

// -----------------------------------------------------------------------------
// PARSING
// -----------------------------------------------------------------------------

int metkit_parse_marsrequest(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_marsrequest(metkit_marsrequest_t** request) {
return tryCatch([request] {
ASSERT(request);
*request = new metkit_marsrequest_t();
});
}

int metkit_free_marsrequest(const metkit_marsrequest_t* request) {
return tryCatch([request] {
ASSERT(request);
ChrisspyB marked this conversation as resolved.
Show resolved Hide resolved
delete request;
});
}

int metkit_marsrequest_add(metkit_marsrequest_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);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Variable names in general could have longer names ;)

std::vector<std::string> vv;
for (int i = 0; i < numValues; i++) {
vv.push_back(std::string(values[i]));
}
ChrisspyB marked this conversation as resolved.
Show resolved Hide resolved
request->values(n, vv);
});
}

int metkit_marsrequest_add1(metkit_marsrequest_t* request, const char* param, const char* value) {
return metkit_marsrequest_add(request, param, &value, 1);
}

int metkit_marsrequest_set_verb(metkit_marsrequest_t* request, const char* verb) {
return tryCatch([request, verb] {
ASSERT(request);
ASSERT(verb);
request->verb(verb);
});
}

int metkit_marsrequest_verb(const metkit_marsrequest_t* request, const char** verb) {
return tryCatch([request, verb] {
ASSERT(request);
ASSERT(verb);
*verb = request->verb().c_str();
});
}

int metkit_marsrequest_has_param(const metkit_marsrequest_t* request, const char* param, bool* has) {
return tryCatch([request, param, has] {
ASSERT(request);
ASSERT(param);
ASSERT(has);
*has = request->has(param);
});
}

int metkit_marsrequest_params(const metkit_marsrequest_t* request, metkit_paramiterator_t** params) {
return tryCatch([request, params] {
ASSERT(request);
ASSERT(params);
*params = new metkit_paramiterator_t(request->params());
});
}

int metkit_marsrequest_count_values(const metkit_marsrequest_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_marsrequest_value(const metkit_marsrequest_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_marsrequest_values(const metkit_marsrequest_t* request, const char* param, const char** values[], size_t* numValues) {
return tryCatch([request, param, values, numValues] {
ASSERT(request);
ASSERT(param);
ASSERT(values);
ASSERT(numValues);
const std::vector<std::string>& v = request->values(param);
*numValues = v.size();
*values = new const char*[v.size()];
for (size_t i = 0; i < v.size(); i++) {
(*values)[i] = v[i].c_str();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-> instead of pointer arithmetic.

}
ChrisspyB marked this conversation as resolved.
Show resolved Hide resolved
});
}

int metkit_marsrequest_expand(const metkit_marsrequest_t* request, metkit_marsrequest_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 = expand.expand(*request);
});
}

int metkit_marsrequest_merge(metkit_marsrequest_t* request, const metkit_marsrequest_t* otherRequest) {
return tryCatch([request, otherRequest] {
ASSERT(request);
ASSERT(otherRequest);
request->merge(*otherRequest);
});
}

// -----------------------------------------------------------------------------
// REQUEST ITERATOR
// -----------------------------------------------------------------------------

int metkit_free_requestiterator(const metkit_requestiterator_t* it) {
return tryCatch([it] {
ASSERT(it);
ChrisspyB marked this conversation as resolved.
Show resolved Hide resolved
delete it;
});
}

int metkit_requestiterator_next(metkit_requestiterator_t* it) {
return tryCatch(std::function<int()>{[it] {
ASSERT(it);
return it->next();
}});
}

int metkit_requestiterator_request(metkit_requestiterator_t* it, metkit_marsrequest_t* request) {
return tryCatch([it, request] {
ASSERT(it);
ASSERT(request);
ASSERT(request->empty());

it->current(request);
});
}

// -----------------------------------------------------------------------------
// PARAM ITERATOR
// -----------------------------------------------------------------------------

int metkit_free_paramiterator(const metkit_paramiterator_t* it) {
return tryCatch([it] {
ASSERT(it);
ChrisspyB marked this conversation as resolved.
Show resolved Hide resolved
delete it;
});
}

int metkit_paramiterator_next(metkit_paramiterator_t* it) {
return tryCatch(std::function<int()>{[it] {
ASSERT(it);
return it->next();
}});
}

int metkit_paramiterator_param(metkit_paramiterator_t* it, const char** param) {
return tryCatch([it, param] {
ASSERT(it);
ASSERT(param);
it->current(param);
});
}


// ---------------------------------------------------------------------------------------------------------------------

} // extern "C"
Loading
Loading