Skip to content

Commit

Permalink
C API wrapper macros
Browse files Browse the repository at this point in the history
The `CAPI_XXX_BEGIN` and `CAPI_XXX_END` macros replace the explicit definitions of C API function with macro-generated text. This is principally to make available the function name as a static string available to the exception wrapper.
  • Loading branch information
eric-hughes-tiledb committed Oct 11, 2023
1 parent 33dd062 commit e8b936b
Show file tree
Hide file tree
Showing 6 changed files with 239 additions and 50 deletions.
75 changes: 29 additions & 46 deletions tiledb/api/c_api/context/context_api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -138,11 +138,9 @@ using tiledb::api::api_entry_with_context;
/*
* API Audit: No channel to return error message (failure code only)
*/
capi_return_t tiledb_ctx_alloc(
tiledb_config_handle_t* config, tiledb_ctx_handle_t** ctx) noexcept {
return tiledb::api::api_entry_plain<tiledb::api::tiledb_ctx_alloc>(
config, ctx);
}
CAPI_PLAIN_BEGIN(
ctx_alloc, tiledb_config_handle_t* config, tiledb_ctx_handle_t** ctx)
CAPI_PLAIN_END(config, ctx)

/*
* We have a special case with tiledb_ctx_alloc_with_error. It's declared in
Expand All @@ -156,57 +154,42 @@ capi_return_t tiledb_ctx_alloc(
*/
extern "C" {

capi_return_t tiledb_ctx_alloc_with_error(
CAPI_ERROR_BEGIN_X(
ctx_alloc_with_error,
ctx_alloc,
tiledb_config_handle_t* config,
tiledb_ctx_handle_t** ctx,
tiledb_error_handle_t** error) noexcept {
/*
* Wrapped with the `api_entry_error` variation. Note that the same function
* is wrapped with `api_entry_plain` above.
*/
return tiledb::api::api_entry_error<tiledb::api::tiledb_ctx_alloc>(
error, config, ctx);
}
tiledb_ctx_handle_t** ctx)
CAPI_ERROR_END(config, ctx)

} // extern "C"

/*
* API Audit: void return
*/
void tiledb_ctx_free(tiledb_ctx_handle_t** ctx) noexcept {
return tiledb::api::api_entry_void<tiledb::api::tiledb_ctx_free>(ctx);
}
CAPI_VOID_BEGIN(ctx_free, tiledb_ctx_handle_t** ctx)
CAPI_VOID_END(ctx)

capi_return_t tiledb_ctx_get_stats(
tiledb_ctx_t* ctx, char** stats_json) noexcept {
return api_entry_with_context<tiledb::api::tiledb_ctx_get_stats>(
ctx, stats_json);
}
CAPI_WITH_CONTEXT_BEGIN(ctx_get_stats, tiledb_ctx_t* ctx, char** stats_json)
CAPI_WITH_CONTEXT_END(ctx, stats_json)

capi_return_t tiledb_ctx_get_config(
tiledb_ctx_t* ctx, tiledb_config_handle_t** config) noexcept {
return api_entry_with_context<tiledb::api::tiledb_ctx_get_config>(
ctx, config);
}
CAPI_WITH_CONTEXT_BEGIN(
ctx_get_config, tiledb_ctx_t* ctx, tiledb_config_handle_t** config)
CAPI_WITH_CONTEXT_END(ctx, config)

capi_return_t tiledb_ctx_get_last_error(
tiledb_ctx_t* ctx, tiledb_error_handle_t** err) noexcept {
return api_entry_with_context<tiledb::api::tiledb_ctx_get_last_error>(
ctx, err);
}
CAPI_WITH_CONTEXT_BEGIN(
ctx_get_last_error, tiledb_ctx_t* ctx, tiledb_error_handle_t** err)
CAPI_WITH_CONTEXT_END(ctx, err)

capi_return_t tiledb_ctx_is_supported_fs(
tiledb_ctx_t* ctx, tiledb_filesystem_t fs, int32_t* is_supported) noexcept {
return api_entry_with_context<tiledb::api::tiledb_ctx_is_supported_fs>(
ctx, fs, is_supported);
}
CAPI_WITH_CONTEXT_BEGIN(
ctx_is_supported_fs,
tiledb_ctx_t* ctx,
tiledb_filesystem_t fs,
int32_t* is_supported)
CAPI_WITH_CONTEXT_END(ctx, fs, is_supported)

capi_return_t tiledb_ctx_cancel_tasks(tiledb_ctx_t* ctx) noexcept {
return api_entry_with_context<tiledb::api::tiledb_ctx_cancel_tasks>(ctx);
}
CAPI_WITH_CONTEXT_BEGIN(ctx_cancel_tasks, tiledb_ctx_t* ctx)
CAPI_WITH_CONTEXT_END(ctx)

capi_return_t tiledb_ctx_set_tag(
tiledb_ctx_t* ctx, const char* key, const char* value) noexcept {
return api_entry_with_context<tiledb::api::tiledb_ctx_set_tag>(
ctx, key, value);
}
CAPI_WITH_CONTEXT_BEGIN(
ctx_set_tag, tiledb_ctx_t* ctx, const char* key, const char* value)
CAPI_WITH_CONTEXT_END(ctx, key, value)
1 change: 1 addition & 0 deletions tiledb/api/c_api_support/c_api_support.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,6 @@

#include "argument_validation.h"
#include "tiledb/api/c_api_support/exception_wrapper/exception_wrapper.h"
#include "tiledb/api/c_api_support/exception_wrapper/hook.h"

#endif // TILEDB_CAPI_SUPPORT_H
15 changes: 12 additions & 3 deletions tiledb/api/c_api_support/exception_wrapper/exception_wrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -514,18 +514,25 @@ using ExceptionActionCtxErr = detail::ExceptionActionDetailCtxErr;
//-------------------------------------------------------
// Exception wrapper
//-------------------------------------------------------
template<auto f>
class CAPIFunctionNullAspect {
public:
template<typename... Args>
static void apply(Args...) {}
};

/**
* Non-specialized wrapper for implementations functions for the C API. May
* only be used as a specialization.
*/
template <auto f, class H>
template <auto f, class H, class A = CAPIFunctionNullAspect<f>>
class CAPIFunction;

/**
* Wrapper for implementations functions for the C API
*/
template <class... Args, capi_return_t (*f)(Args...), class H>
class CAPIFunction<f, H> {
template <class... Args, capi_return_t (*f)(Args...), class H, class A>
class CAPIFunction<f, H, A> {
public:
/**
* Forwarded alias to template parameter H.
Expand Down Expand Up @@ -561,6 +568,8 @@ class CAPIFunction<f, H> {
* Note that we don't need std::forward here because all the arguments
* must have "C" linkage.
*/
//-------------------------------------------------------
A::apply(f, args...);
auto x{f(args...)};
h.action_on_success();
return x;
Expand Down
114 changes: 114 additions & 0 deletions tiledb/api/c_api_support/exception_wrapper/hook.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/**
* @file tiledb/api/c_api_support/exception_wrapper/hook.h
*
* @section LICENSE
*
* The MIT License
*
* @copyright Copyright (c) 2023 TileDB, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @section DESCRIPTION
*
* This file defines macros to define C API functions.
*/

#ifndef TILEDB_EXCEPTION_WRAPPER_HOOK_H
#define TILEDB_EXCEPTION_WRAPPER_HOOK_H


/*
* The macro wrappers all have the same form
* ```
* CAPI_XXX_BEGIN(name, ...)
* CAPI_XXX_END(...)
* ```
* - The name argument is for the base name, that is, the name without the
* `tiledb_` prefix.
* - The `__VA_ARGS__` argument in `*_BEGIN` is the full argument part of the
* function signature. By "full", that means it requires that parameter names
* be present.
* - The `__VA_ARGS__` argument in `*_END` are the call arguments, that is, it's
* the function signature arguments without their type declarations.
*/

/// clang-format off

#define CAPI_DEFN(name) tiledb_##name
#define CAPI_IMPL(name) tiledb::api::tiledb_##name
#define CAPI_XFMR(name) tiledb::api::api_entry_##name

#define CAPI_BEGIN(root, ret) ret CAPI_DEFN(root)
#define CAPI_MIDDLE(root, xfmr) \
noexcept { \
return CAPI_XFMR(xfmr)<CAPI_IMPL(root)>
#define CAPI_END(...) \
(__VA_ARGS__); \
}

#define CAPI_PLAIN_BEGIN(root, ...) \
CAPI_BEGIN(root, capi_return_t )(__VA_ARGS__) \
CAPI_MIDDLE(root, plain)
#define CAPI_PLAIN_END(...) \
CAPI_END(__VA_ARGS__)

#define CAPI_VOID_BEGIN(root, ...) \
CAPI_BEGIN(root, void )(__VA_ARGS__) \
CAPI_MIDDLE(root, void)
#define CAPI_VOID_END(...) \
CAPI_END(__VA_ARGS__)

#define CAPI_WITH_CONTEXT_BEGIN(root, ...) \
CAPI_BEGIN(root, capi_return_t)(__VA_ARGS__) \
CAPI_MIDDLE(root, with_context)
#define CAPI_WITH_CONTEXT_END(...) \
CAPI_END(__VA_ARGS__)

#define CAPI_CONTEXT_BEGIN(root, ...) \
CAPI_BEGIN(root, capi_return_t)(__VA_ARGS__) \
CAPI_MIDDLE(root, context)
#define CAPI_CONTEXT_END(...) \
CAPI_END(__VA_ARGS__)

/*
* The argument lists for the ERROR macros omit the `error` argument.
*
* The ERROR wrapper is different from the others because it makes a special
* case of the `error` argument. Its definition is at the end of the argument
* list of the signature but at the beginning of the argument list of the call.
*
* Note that these macros, as written, do not handle zero-length variable
* arguments. There are no cases where this is needed.
*/
#define CAPI_ERROR_BEGIN(root, ...) \
CAPI_BEGIN(root, capi_return_t)(__VA_ARGS__, tiledb_error_t * *error) \
CAPI_MIDDLE(root, error)
#define CAPI_ERROR_END(...) \
CAPI_END(error, __VA_ARGS__)
/*
* Special case where the API name and wrapped function are not identical.
*/
#define CAPI_ERROR_BEGIN_X(root, root2, ...) \
CAPI_BEGIN(root, capi_return_t)(__VA_ARGS__, tiledb_error_t * *error) \
CAPI_MIDDLE(root2, error)

/// clang-format on

#endif // TILEDB_EXCEPTION_WRAPPER_HOOK_H
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ include(unit_test)
commence(unit_test capi_exception_wrapper)
this_target_sources(
unit_capi_exception_wrapper.cc
unit_capi_error_tree.cc)
unit_capi_error_tree.cc
unit_capi_hook.cc)
this_target_object_libraries(
# The exception wrapper is wrapped up with the context, so that's the
# top-level dependency required for the test.
Expand Down
81 changes: 81 additions & 0 deletions tiledb/api/c_api_support/exception_wrapper/test/unit_capi_hook.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/**
* @file c_api_support/exception_wrapper/test/unit_capi_hook.cc
*
* @section LICENSE
*
* The MIT License
*
* @copyright Copyright (c) 2023 TileDB, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @section DESCRIPTION
*/

#include <test/support/tdb_catch.h>

#include "../exception_wrapper.h"

using namespace tiledb::api;
using namespace tiledb::api::detail;

//-------------------------------------------------------
// Hook
//-------------------------------------------------------

class LABase {
protected:
static std::string msg;

public:
static void reset() {
msg = "";
}
static std::string message() {
return msg;
}
};

std::string LABase::msg;

template <auto f>
class LoggingAspect : public LABase {
public:
template <typename... Args>
static void apply(Args...) {
msg += "something: " + std::string(typeid(f).name());
}
};
capi_return_t tf_null() {
return TILEDB_OK;
}
using null_wrapped_for_logging = tiledb::api::
CAPIFunction<tf_null, tiledb::api::ExceptionAction, LoggingAspect<tf_null>>;

TEST_CASE("Hook 0", "[hook]") {
LABase::reset();
CHECK(LABase::message() == "");
tiledb::api::ExceptionAction h;
null_wrapped_for_logging().function(h);
// CHECK(LABase::message()== "something");
}

//------------------------------------------------------
// C API definition functions
//-------------------------------------------------------

0 comments on commit e8b936b

Please sign in to comment.