Refactored etl::error_handler to use etl::delegate style implementation.

Allows set_callback() function to be given run-time and compile-time pointers to free and member functions without using etl::ifunction.
This commit is contained in:
John Wellbelove 2020-06-26 10:48:14 +01:00
parent eabe328398
commit a27508ca96
5 changed files with 278 additions and 47 deletions

View File

@ -47,14 +47,34 @@ namespace etl
{
namespace private_error_handler
{
template <class dummy>
// A wrapper template to allow static definition in header.
template <typename TDummy>
struct wrapper
{
static etl::ifunction<const etl::exception&>* p_ifunction;
using stub_type = void(*)(void* object, const etl::exception&);
//*************************************************************************
/// The internal invocation object.
//*************************************************************************
struct invocation_element
{
//***********************************************************************
invocation_element()
: object(ETL_NULLPTR)
, stub(ETL_NULLPTR)
{
}
//***********************************************************************
void* object;
stub_type stub;
};
static invocation_element invocation;
};
template <class dummy>
etl::ifunction<const etl::exception&>* wrapper<dummy>::p_ifunction = ETL_NULLPTR;
template <typename TDummy>
typename wrapper<TDummy>::invocation_element wrapper<TDummy>::invocation;
}
//***************************************************************************
@ -94,7 +114,52 @@ namespace etl
//*****************************************************************************
static void set_callback(ifunction<const etl::exception&>& f)
{
private_error_handler::wrapper<void>::p_ifunction = &f;
create((void*)(&f), ifunction_stub);
}
//*************************************************************************
/// Create from function (Compile time).
//*************************************************************************
template <void(*Method)(const etl::exception&)>
static void set_callback()
{
create(ETL_NULLPTR, function_stub<Method>);
}
//*************************************************************************
/// Create from instance method (Run time).
//*************************************************************************
template <typename T, void(T::* Method)(const etl::exception&)>
static void set_callback(T& instance)
{
create((void*)(&instance), method_stub<T, Method>);
}
//*************************************************************************
/// Create from const instance method (Run time).
//*************************************************************************
template <typename T, void(T::* Method)(const etl::exception&) const>
static void set_callback(const T& instance)
{
create((void*)(&instance), const_method_stub<T, Method>);
}
//*************************************************************************
/// Create from instance method (Compile time).
//*************************************************************************
template <typename T, T& Instance, void(T::* Method)(const etl::exception&)>
static void set_callback()
{
create(method_instance_stub<T, Instance, Method>);
}
//*************************************************************************
/// Create from const instance method (Compile time).
//*************************************************************************
template <typename T, T const& Instance, void(T::* Method)(const etl::exception&) const>
static void set_callback()
{
create(const_method_instance_stub<T, Instance, Method>);
}
//*****************************************************************************
@ -103,11 +168,99 @@ namespace etl
//*****************************************************************************
static void error(const etl::exception& e)
{
if (private_error_handler::wrapper<void>::p_ifunction != ETL_NULLPTR)
if (private_error_handler::wrapper<void>::invocation.stub != ETL_NULLPTR)
{
(*private_error_handler::wrapper<void>::p_ifunction)(e);
(*private_error_handler::wrapper<void>::invocation.stub)(private_error_handler::wrapper<void>::invocation.object, e);
}
}
private:
using stub_type = void(*)(void* object, const etl::exception&);
//*************************************************************************
/// The internal invocation object.
//*************************************************************************
struct invocation_element
{
//***********************************************************************
void* object = ETL_NULLPTR;
stub_type stub = ETL_NULLPTR;
};
//*************************************************************************
/// Constructs a callback from an object and stub.
//*************************************************************************
static void create(void* object, stub_type stub)
{
private_error_handler::wrapper<void>::invocation.object = object;
private_error_handler::wrapper<void>::invocation.stub = stub;
}
//*************************************************************************
/// Constructs a callback from a stub.
//*************************************************************************
static void create(stub_type stub)
{
private_error_handler::wrapper<void>::invocation.object = ETL_NULLPTR;
private_error_handler::wrapper<void>::invocation.stub = stub;
}
//*************************************************************************
/// Stub call for a member function. Run time instance.
//*************************************************************************
template <typename T, void(T::* Method)(const etl::exception&)>
static void method_stub(void* object, const etl::exception& e)
{
T* p = static_cast<T*>(object);
return (p->*Method)(e);
}
//*************************************************************************
/// Stub call for a const member function. Run time instance.
//*************************************************************************
template <typename T, void(T::* Method)(const etl::exception&) const>
static void const_method_stub(void* object, const etl::exception& e)
{
T* const p = static_cast<T*>(object);
return (p->*Method)(e);
}
//*************************************************************************
/// Stub call for a member function. Compile time instance.
//*************************************************************************
template <typename T, T& Instance, void(T::* Method)(const etl::exception&)>
static void method_instance_stub(void*, const etl::exception& e)
{
return (Instance.*Method)(e);
}
//*************************************************************************
/// Stub call for a const member function. Compile time instance.
//*************************************************************************
template <typename T, const T& Instance, void(T::* Method)(const etl::exception&) const>
static void const_method_instance_stub(void*, const etl::exception& e)
{
(Instance.*Method)(e);
}
//*************************************************************************
/// Stub call for a free function.
//*************************************************************************
template <void(*Method)(const etl::exception&)>
static void function_stub(void*, const etl::exception& e)
{
(Method)(e);
}
//*************************************************************************
/// Stub call for a ifunction. Run time instance.
//*************************************************************************
static void ifunction_stub(void* object, const etl::exception& e)
{
etl::ifunction<const etl::exception&>* p = static_cast<etl::ifunction<const etl::exception&>*>(object);
p->operator()(e);
}
};
}
@ -134,13 +287,8 @@ namespace etl
#endif
#else
#if defined(ETL_LOG_ERRORS)
#if defined(NDEBUG)
#define ETL_ASSERT(b, e) {if(!(b)) {etl::error_handler::error((e));}} // If the condition fails, calls the error handler
#define ETL_ALWAYS_ASSERT(e) {etl::error_handler::error((e));} // Calls the error handler
#else
#define ETL_ASSERT(b, e) {if(!(b)) {etl::error_handler::error((e)); assert(false);}} // If the condition fails, calls the error handler then asserts.
#define ETL_ALWAYS_ASSERT(e) {etl::error_handler::error((e)); assert(false);} // Calls the error handler then asserts.
#endif
#define ETL_ASSERT(b, e) {if(!(b)) {etl::error_handler::error((e));}} // If the condition fails, calls the error handler
#define ETL_ALWAYS_ASSERT(e) {etl::error_handler::error((e));} // Calls the error handler
#else
#if defined(NDEBUG)
#define ETL_ASSERT(b, e) // Does nothing.

View File

@ -1,6 +1,6 @@
{
"name": "Embedded Template Library",
"version": "18.3.4",
"version": "18.4.0",
"authors": {
"name": "John Wellbelove",
"email": "john.wellbelove@etlcpp.com"

View File

@ -1,5 +1,5 @@
name=Embedded Template Library
version=18.3.4
version=18.4.0
author= John Wellbelove <john.wellbelove@etlcpp.com>
maintainer=John Wellbelove <john.wellbelove@etlcpp.com>
license=MIT

View File

@ -1,3 +1,8 @@
===============================================================================
18.4.0
Refactored etl::error_handler to use etl::delegate style implementation.
Allows set_callback() function to be given run-time and compile-time pointers to free and member functions without using etl::ifunction.
===============================================================================
18.3.4
Changed std::move to etl::move in std::optional and std::queue

View File

@ -34,52 +34,66 @@ SOFTWARE.
#include "etl/error_handler.h"
#include "etl/exception.h"
bool error_received;
//*****************************************************************************
// An exception.
//*****************************************************************************
class test_exception : public etl::exception
namespace
{
public:
bool error_received;
test_exception(string_type file_name_, numeric_type line_number_)
: exception(ETL_ERROR_TEXT("test_exception", "123"), file_name_, line_number_)
//*****************************************************************************
// An exception.
//*****************************************************************************
class test_exception : public etl::exception
{
error_received = false;
}
};
public:
//*****************************************************************************
// A free error handler function.
//*****************************************************************************
void receive_error(const etl::exception& e)
{
error_received = true;
CHECK(strcmp(e.what(), "test_exception") == 0);
}
test_exception(string_type file_name_, numeric_type line_number_)
: exception(ETL_ERROR_TEXT("test_exception", "123"), file_name_, line_number_)
{
error_received = false;
}
};
//*****************************************************************************
class test_class
{
public:
//***************************************************************************
// A member error handler function.
//***************************************************************************
//*****************************************************************************
// A free error handler function.
//*****************************************************************************
void receive_error(const etl::exception& e)
{
error_received = true;
CHECK(strcmp(e.what(), "test_exception") == 0);
}
};
//*****************************************************************************
class test_class
{
public:
//***************************************************************************
// A member error handler function.
//***************************************************************************
void receive_error(const etl::exception& e)
{
error_received = true;
CHECK(strcmp(e.what(), "test_exception") == 0);
}
//***************************************************************************
// A const member error handler function.
//***************************************************************************
void receive_error_const(const etl::exception& e) const
{
error_received = true;
CHECK(strcmp(e.what(), "test_exception") == 0);
}
};
test_class static_test;
}
namespace
{
SUITE(test_error_handler)
{
//*************************************************************************
TEST(test_free_handler_function)
TEST(test_free_handler_function_deprecated)
{
// Create the function callback object.
etl::error_handler::free_function error_callback(receive_error);
@ -94,7 +108,7 @@ namespace
}
//*************************************************************************
TEST(test_member_handler_function)
TEST(test_member_handler_function_deprecated)
{
// Create the class that contains the handler.
test_class test;
@ -110,5 +124,69 @@ namespace
CHECK(error_received);
}
//*************************************************************************
TEST(test_free_handler_function_compile_time)
{
// Tell the error handler about it.
etl::error_handler::set_callback<receive_error>();
// Log an error.
etl::error_handler::error(ETL_ERROR(test_exception));
CHECK(error_received);
}
//*************************************************************************
TEST(test_member_handler_function_run_time)
{
test_class test;
// Tell the error handler about it.
etl::error_handler::set_callback<test_class, &test_class::receive_error>(test);
// Log an error.
etl::error_handler::error(ETL_ERROR(test_exception));
CHECK(error_received);
}
//*************************************************************************
TEST(test_const_member_handler_function_run_time)
{
test_class test;
// Tell the error handler about it.
etl::error_handler::set_callback<test_class, &test_class::receive_error_const>(test);
// Log an error.
etl::error_handler::error(ETL_ERROR(test_exception));
CHECK(error_received);
}
//*************************************************************************
TEST(test_member_handler_function_compile_time)
{
// Tell the error handler about it.
etl::error_handler::set_callback<test_class, static_test, &test_class::receive_error>();
// Log an error.
etl::error_handler::error(ETL_ERROR(test_exception));
CHECK(error_received);
}
//*************************************************************************
TEST(test_const_member_handler_function_compile_time)
{
// Tell the error handler about it.
etl::error_handler::set_callback<test_class, static_test, &test_class::receive_error_const>();
// Log an error.
etl::error_handler::error(ETL_ERROR(test_exception));
CHECK(error_received);
}
};
}