Skip to content

Callback extension and optimisation #12036

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

Merged
merged 3 commits into from
Mar 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
215 changes: 202 additions & 13 deletions TESTS/mbed_functional/callback/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,27 @@
#include "greentea-client/test_env.h"
#include "unity.h"
#include "utest.h"
#include <mstd_functional>

using namespace utest::v1;

template <typename T>
using func0_type = T();

template <typename T>
using func1_type = T(T);

template <typename T>
using func2_type = T(T, T);

template <typename T>
using func3_type = T(T, T, T);

template <typename T>
using func4_type = T(T, T, T, T);

template <typename T>
using func5_type = T(T, T, T, T, T);

// static functions
template <typename T>
Expand Down Expand Up @@ -523,13 +541,17 @@ void test_dispatch0()
Verifier<T>::verify0(&const_volatile_void_func0<T>, (const volatile Thing<T> *)&thing);
Verifier<T>::verify0(callback(static_func0<T>));

Callback<T()> cb(static_func0);
Callback<T()> cb(static_func0<T>);
Verifier<T>::verify0(cb);
cb = static_func0;
TEST_ASSERT_TRUE(cb);
func0_type<T> *p = nullptr;
cb = p;
TEST_ASSERT_FALSE(cb);
cb = static_func0<T>;
Verifier<T>::verify0(cb);
cb = {&bound_func0<T>, &thing};
Verifier<T>::verify0(&cb, &Callback<T()>::call);
Verifier<T>::verify0(&Callback<T()>::thunk, (void *)&cb);
Verifier<T>::verify0(&Callback<T()>::thunk, &cb);
}

template <typename T>
Expand All @@ -554,9 +576,13 @@ void test_dispatch1()
Verifier<T>::verify1(&const_volatile_void_func1<T>, (const volatile Thing<T> *)&thing);
Verifier<T>::verify1(callback(static_func1<T>));

Callback<T(T)> cb(static_func1);
Callback<T(T)> cb(static_func1<T>);
Verifier<T>::verify1(cb);
cb = static_func1;
TEST_ASSERT_TRUE(cb);
func1_type<T> *p = nullptr;
cb = p;
TEST_ASSERT_FALSE(cb);
cb = static_func1<T>;
Verifier<T>::verify1(cb);
cb = {&bound_func1<T>, &thing};
Verifier<T>::verify1(&cb, &Callback<T(T)>::call);
Expand Down Expand Up @@ -585,9 +611,13 @@ void test_dispatch2()
Verifier<T>::verify2(&const_volatile_void_func2<T>, (const volatile Thing<T> *)&thing);
Verifier<T>::verify2(callback(static_func2<T>));

Callback<T(T, T)> cb(static_func2);
Callback<T(T, T)> cb(static_func2<T>);
Verifier<T>::verify2(cb);
cb = static_func2;
TEST_ASSERT_TRUE(cb);
func2_type<T> *p = nullptr;
cb = p;
TEST_ASSERT_FALSE(cb);
cb = static_func2<T>;
Verifier<T>::verify2(cb);
cb = {&bound_func2<T>, &thing};
Verifier<T>::verify2(&cb, &Callback<T(T, T)>::call);
Expand Down Expand Up @@ -616,9 +646,13 @@ void test_dispatch3()
Verifier<T>::verify3(&const_volatile_void_func3<T>, (const volatile Thing<T> *)&thing);
Verifier<T>::verify3(callback(static_func3<T>));

Callback<T(T, T, T)> cb(static_func3);
Callback<T(T, T, T)> cb(static_func3<T>);
Verifier<T>::verify3(cb);
cb = static_func3;
TEST_ASSERT_TRUE(cb);
func3_type<T> *p = nullptr;
cb = p;
TEST_ASSERT_FALSE(cb);
cb = static_func3<T>;
Verifier<T>::verify3(cb);
cb = {&bound_func3<T>, &thing};
Verifier<T>::verify3(&cb, &Callback<T(T, T, T)>::call);
Expand Down Expand Up @@ -647,9 +681,13 @@ void test_dispatch4()
Verifier<T>::verify4(&const_volatile_void_func4<T>, (const volatile Thing<T> *)&thing);
Verifier<T>::verify4(callback(static_func4<T>));

Callback<T(T, T, T, T)> cb(static_func4);
Callback<T(T, T, T, T)> cb(static_func4<T>);
Verifier<T>::verify4(cb);
cb = static_func4;
TEST_ASSERT_TRUE(cb);
func4_type<T> *p = nullptr;
cb = p;
TEST_ASSERT_FALSE(cb);
cb = static_func4<T>;
Verifier<T>::verify4(cb);
cb = {&bound_func4<T>, &thing};
Verifier<T>::verify4(&cb, &Callback<T(T, T, T, T)>::call);
Expand Down Expand Up @@ -678,15 +716,159 @@ void test_dispatch5()
Verifier<T>::verify5(&const_volatile_void_func5<T>, (const volatile Thing<T> *)&thing);
Verifier<T>::verify5(callback(static_func5<T>));

Callback<T(T, T, T, T, T)> cb(static_func5);
Callback<T(T, T, T, T, T)> cb(static_func5<T>);
Verifier<T>::verify5(cb);
cb = static_func5;
TEST_ASSERT_TRUE(cb);
func5_type<T> *p = nullptr;
cb = p;
TEST_ASSERT_FALSE(cb);
cb = static_func5<T>;
Verifier<T>::verify5(cb);
cb = {&bound_func5<T>, &thing};
Verifier<T>::verify5(&cb, &Callback<T(T, T, T, T, T)>::call);
#if 0
Verifier<T>::verify5(&Callback<T(T, T, T, T, T)>::thunk, (void *)&cb);
#endif
}

#include <mstd_functional>

struct TrivialFunctionObject {
TrivialFunctionObject(int n) : val(n)
{
}

int operator()(int x) const
{
return x + val;
}
private:
int val;
};

/* Exact count of copy, construction and destruction may vary depending on
* copy elision by compiler, but the derived live count can be relied upon.
*/
static int construct_count;
static int destruct_count;
static int copy_count;

static int live_count()
{
return construct_count - destruct_count;
}

struct FunctionObject {
FunctionObject(int n) : val(n)
{
construct_count++;
}

FunctionObject(const FunctionObject &other) : val(other.val)
{
construct_count++;
copy_count++;
}

~FunctionObject()
{
destruct_count++;
destroyed = true;
}

int operator()(int x) const
{
return destroyed ? -1000 : x + val;
}
private:
const int val;
bool destroyed = false;
};

void test_trivial()
{
TrivialFunctionObject fn(1);
TEST_ASSERT_EQUAL(2, fn(1));
Callback<int(int)> cb(fn);
TEST_ASSERT_TRUE(cb);
TEST_ASSERT_EQUAL(2, cb(1));
fn = 5;
TEST_ASSERT_EQUAL(6, fn(1));
TEST_ASSERT_EQUAL(2, cb(1));
cb = std::ref(fn);
fn = 10;
TEST_ASSERT_EQUAL(11, fn(1));
TEST_ASSERT_EQUAL(11, cb(1));
cb = TrivialFunctionObject(3);
TEST_ASSERT_EQUAL(7, cb(4));
cb = nullptr;
TEST_ASSERT_FALSE(cb);
cb = TrivialFunctionObject(7);
Callback<int(int)> cb2(cb);
TEST_ASSERT_EQUAL(8, cb(1));
TEST_ASSERT_EQUAL(9, cb2(2));
cb2 = cb;
TEST_ASSERT_EQUAL(6, cb2(-1));
cb = cb;
TEST_ASSERT_EQUAL(8, cb(1));
cb = std::negate<int>();
TEST_ASSERT_EQUAL(-4, cb(4));
cb = [](int x) {
return x - 7;
};
TEST_ASSERT_EQUAL(1, cb(8));
cb = cb2 = nullptr;
TEST_ASSERT_FALSE(cb);
}

#if MBED_CONF_PLATFORM_CALLBACK_NONTRIVIAL
void test_nontrivial()
{
{
FunctionObject fn(1);
TEST_ASSERT_EQUAL(1, construct_count);
TEST_ASSERT_EQUAL(0, destruct_count);
TEST_ASSERT_EQUAL(2, fn(1));
Callback<int(int)> cb(fn);
TEST_ASSERT_TRUE(cb);
TEST_ASSERT_EQUAL(2, live_count());
TEST_ASSERT_EQUAL(2, cb(1));
cb = std::ref(fn);
TEST_ASSERT_EQUAL(1, live_count());
TEST_ASSERT_EQUAL(5, cb(4));
cb = FunctionObject(3);
TEST_ASSERT_EQUAL(2, live_count());
TEST_ASSERT_EQUAL(7, cb(4));
cb = nullptr;
TEST_ASSERT_FALSE(cb);
TEST_ASSERT_EQUAL(1, live_count());
cb = FunctionObject(7);
TEST_ASSERT_EQUAL(2, live_count());
int old_copy_count = copy_count;
Callback<int(int)> cb2(cb);
TEST_ASSERT_EQUAL(old_copy_count + 1, copy_count);
TEST_ASSERT_EQUAL(3, live_count());
TEST_ASSERT_EQUAL(8, cb(1));
TEST_ASSERT_EQUAL(9, cb2(2));
old_copy_count = copy_count;
cb2 = cb;
TEST_ASSERT_EQUAL(old_copy_count + 1, copy_count);
TEST_ASSERT_EQUAL(3, live_count());
TEST_ASSERT_EQUAL(6, cb2(-1));
int old_construct_count = construct_count;
old_copy_count = copy_count;
cb = cb;
TEST_ASSERT_EQUAL(3, live_count());
TEST_ASSERT_EQUAL(old_construct_count, construct_count);
TEST_ASSERT_EQUAL(old_copy_count, copy_count);
cb = cb2 = nullptr;
TEST_ASSERT_FALSE(cb);
TEST_ASSERT_EQUAL(1, live_count());
}
TEST_ASSERT_EQUAL(0, live_count());
}
#endif


// Test setup
utest::v1::status_t test_setup(const size_t number_of_cases)
Expand All @@ -702,7 +884,10 @@ Case cases[] = {
Case("Testing callbacks with 2 uint64s", test_dispatch2<uint64_t>),
Case("Testing callbacks with 3 uint64s", test_dispatch3<uint64_t>),
Case("Testing callbacks with 4 uint64s", test_dispatch4<uint64_t>),
// IAR currently crashes at link time with this test - skip it as it's well beyond anything needed by real code
#ifndef __ICCARM__
Case("Testing callbacks with 5 uint64s", test_dispatch5<uint64_t>),
#endif
#elif DO_SMALL_TEST
Case("Testing callbacks with 0 uchars", test_dispatch0<unsigned char>),
Case("Testing callbacks with 1 uchars", test_dispatch1<unsigned char>),
Expand All @@ -717,6 +902,10 @@ Case cases[] = {
Case("Testing callbacks with 3 ints", test_dispatch3<int>),
Case("Testing callbacks with 4 ints", test_dispatch4<int>),
Case("Testing callbacks with 5 ints", test_dispatch5<int>),
Case("Testing trivial function object", test_trivial),
#if MBED_CONF_PLATFORM_CALLBACK_NONTRIVIAL
Case("Testing non-trivial function object", test_nontrivial),
#endif
#endif
};

Expand Down
6 changes: 5 additions & 1 deletion TESTS/mbed_platform/Transaction/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,11 @@ void test_Transaction_init()
TEST_ASSERT_EQUAL(rx_buffer_size, test_transaction.get_transaction()->rx_length);
TEST_ASSERT_EQUAL(event_id, test_transaction.get_transaction()->event);
TEST_ASSERT_EQUAL(word_width, test_transaction.get_transaction()->width);
TEST_ASSERT_EQUAL(callback, test_transaction.get_transaction()->callback);
#if MBED_CONF_PLATFORM_CALLBACK_COMPARABLE
TEST_ASSERT_TRUE(callback == test_transaction.get_transaction()->callback);
#else
TEST_ASSERT_FALSE(nullptr == test_transaction.get_transaction()->callback)
#endif
}

/** Test Transaction class - creation without initialisation
Expand Down
2 changes: 0 additions & 2 deletions TESTS/network/emac/emac_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,6 @@ char emac_if_get_trace_level();

void emac_if_trace_to_ascii_hex_dump(const char *prefix, int len, char *data);

void emac_if_link_state_change_cb(void *data, bool up);

unsigned char *emac_if_get_own_addr(void);

int emac_if_get_mtu_size();
Expand Down
2 changes: 2 additions & 0 deletions UNITTESTS/features/netsocket/NetworkInterface/unittest.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
# UNIT TESTS
####################

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DMBED_CONF_PLATFORM_CALLBACK_COMPARABLE")

# Source files
set(unittest-sources
../features/netsocket/SocketAddress.cpp
Expand Down
56 changes: 56 additions & 0 deletions UNITTESTS/target_h/platform/cxxsupport/mstd_new
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/* mbed Microcontroller Library
* Copyright (c) 2019 ARM Limited
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef MSTD_NEW_
#define MSTD_NEW_

/* <mstd_new>
*
* - includes toolchain's <new>
* - For all toolchains, C++17 backports:
* - mstd::launder
*/

#include <new>
#if __cpp_lib_launder < 201606
#include <type_traits>
#endif

namespace mstd
{
using std::nothrow_t;
using std::nothrow;
using std::new_handler;
using std::set_new_handler;

#if __cpp_lib_launder >= 201606
using std::launder;
#else
template <typename T>
constexpr T *launder(T *p) noexcept
{
static_assert(!std::is_function<T>::value && !std::is_void<T>::value, "Can only launder complete object types");
#if defined __clang__ || __GNUC__ >= 9
return __builtin_launder(p);
#else
return p;
#endif
}
#endif

} // namespace mstd

#endif // MSTD_NEW_
Loading