Skip to content

Commit

Permalink
closure const ref param unfinished
Browse files Browse the repository at this point in the history
  • Loading branch information
milyin committed Jul 20, 2023
1 parent 5653236 commit 91f829e
Show file tree
Hide file tree
Showing 2 changed files with 278 additions and 71 deletions.
109 changes: 38 additions & 71 deletions include/zenohcxx/base.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -150,89 +150,56 @@ template <typename ZC_CLOSURE_TYPE, typename ZC_PARAM, typename ZCPP_PARAM,
typename std::enable_if_t<std::is_base_of_v<ZC_PARAM, ZCPP_PARAM> && sizeof(ZC_PARAM) == sizeof(ZCPP_PARAM),
bool> = true>
class ClosureConstRefParam : public Owned<ZC_CLOSURE_TYPE> {
typedef decltype((*ZC_CLOSURE_TYPE::call)(nullptr, nullptr)) ZC_RETVAL;

public:
using Owned<ZC_CLOSURE_TYPE>::Owned;

// Closure is valid if it can be called. The drop operation is optional
bool check() const { return Owned<ZC_CLOSURE_TYPE>::_0.call != nullptr; }

// Call closure with pointer to C parameter
void call(ZC_PARAM* v) {
ZC_RETVAL call(ZC_PARAM* v) {
if (check()) Owned<ZC_CLOSURE_TYPE>::_0.call(v, Owned<ZC_CLOSURE_TYPE>::_0.context);
}

// Call closure with reference to C++ parameter
void operator()(const ZCPP_PARAM& v) { return call(&(static_cast<const ZC_PARAM&>(v))); }
// Call closure with const reference to C++ parameter
ZC_RETVAL operator()(const ZCPP_PARAM& v) { return call(&(static_cast<const ZC_PARAM&>(v))); }

// Construct empty closure
ClosureConstRefParam() : Owned<ZC_CLOSURE_TYPE>(nullptr) {}

// Construct closure from the data handler: any object with operator()(const ZCPP_PARAM&) defined
template <typename T>
ClosureConstRefParam(T&& obj) : Owned<ZC_CLOSURE_TYPE>(wrap_call(std::forward<T>(obj), nullptr)) {}

private:
template <typename T>
ZC_CLOSURE_TYPE wrap_call(T& obj, ZC_CLOSURE_TYPE* prev) {
auto context = new std::pair{&obj, ClosureMoveParam(prev)};
auto call = [](ZC_PARAM* pvalue, void* ctx) {
auto pair = static_cast<std::pair<T*, ClosureConstRefParam>*>(ctx);
ZCPP_PARAM param(pvalue);
(*pair->first)(std::move(param));
return pair->second(std::move(param));
};
auto drop = [](void* ctx) {
auto pair = static_cast<std::pair<T*, ClosureConstRefParam>*>(ctx);
delete pair;
};
return {context, call, drop};
}
ClosureConstRefParam(T&& obj) : Owned<ZC_CLOSURE_TYPE>(wrap_call(std::forward<T>(obj))) {}

template <typename T>
ZC_CLOSURE_TYPE wrap_call(T&& obj, ZC_CLOSURE_TYPE* prev) {
auto context = new std::pair{new T(std::move(obj)), ClosureConstRefParam(prev)};
auto call = [](ZC_PARAM* pvalue, void* ctx) {
auto pair = static_cast<std::pair<T*, ClosureConstRefParam>*>(ctx);
ZCPP_PARAM param(pvalue);
(*pair->first)(std::move(param));
return pair->second(std::move(param));
};
auto drop = [](void* ctx) {
auto pair = static_cast<std::pair<T*, ClosureConstRefParam>*>(ctx);
delete pair->first;
delete pair;
};
return {context, call, drop};
}

template <typename T>
ZC_CLOSURE_TYPE wrap_drop(T& obj, ZC_CLOSURE_TYPE* prev) {
auto context = new std::pair{&obj, ClosureMoveParam(prev)};
auto call = [](ZC_PARAM* pvalue, void* ctx) {
auto pair = static_cast<std::pair<T*, ClosureConstRefParam>*>(ctx);
return pair->second.call(pvalue);
};
auto drop = [](void* ctx) {
auto pair = static_cast<std::pair<T*, ClosureConstRefParam>*>(ctx);
(*pair->first)();
delete pair;
};
return {context, call, drop};
}
// Construct closure from the data handler and the drop handler
// data handler is any object with operator()(const ZCPP_PARAM&) defined
// drop handler is any object with operator()() defined
//
// Drop handler is convenient when it's necessary to catch dropping of the closure costructed from function pointer,
// object lvalue reference or lambda. If the closure holds the user's object, the additional drop handler is
// probably excessive. The cleanup in this case may be done in the object's destructor.
template <typename T, typename D>
ClosureConstRefParam(T&& on_call, D&& on_drop)
: Owned<ZC_CLOSURE_TYPE>(wrap_call(std::forward<T>(on_call), std::forward<D>(on_drop))) {}

template <typename T>
ZC_CLOSURE_TYPE wrap_drop(T&& obj, ZC_CLOSURE_TYPE* prev) {
auto context = new std::pair{new T(std::move(obj)), ClosureConstRefParam(prev)};
auto call = [](ZC_PARAM* pvalue, void* ctx) {
auto pair = static_cast<std::pair<T*, ClosureConstRefParam>*>(ctx);
return pair->second.call(pvalue);
};
auto drop = [](void* ctx) {
auto pair = static_cast<std::pair<T*, ClosureConstRefParam>*>(ctx);
(*pair->first)();
delete pair->first;
delete pair;
private:
typedef ZC_RETVAL (*CALL)(const ZCPP_PARAM& pvalue);
struct Call {
CALL func;
};
typedef void (*DROP)();
struct Drop {
DROP func;
};
ZC_CLOSURE_TYPE wrap_call(CALL on_call) {
auto context = new Call{on_call};
auto call = [](const ZC_PARAM* pvalue, void* ctx) -> ZC_RETVAL {
auto on_call = static_cast<Call*>(ctx);
return on_call->func(*static_cast<const ZCPP_PARAM*>(pvalue));
};
auto drop = [](void* ctx) { delete static_cast<Call*>(ctx); };
return {context, call, drop};
}
};
Expand Down Expand Up @@ -277,12 +244,12 @@ class ClosureMoveParam : public Owned<ZC_CLOSURE_TYPE> {
// data handler: any object with operator()(ZCPP_PARAM&&) defined
// drop handler: any object with operator()() defined
//
// This is convenient when it's necessary to handle drop of the closure, costructed from function pointer or lambda.
// If the closure is constructed from the user's object. the additional drop handler is probably excessive: the
// cleanup may be done in the user's object destructor.
template <typename T1, typename T2>
ClosureMoveParam(T1&& on_call, T2&& on_drop)
: Owned<ZC_CLOSURE_TYPE>(wrap_call(std::forward<T1>(on_call), std::forward<T2>(on_drop))) {}
// Drop handler is convenient when it's necessary to catch dropping of the closure costructed from function pointer,
// object lvalue reference or lambda. If the closure holds the user's object, the additional drop handler is
// probably excessive. The cleanup in this case may be done in the object's destructor.
template <typename T, typename D>
ClosureMoveParam(T&& on_call, D&& on_drop)
: Owned<ZC_CLOSURE_TYPE>(wrap_call(std::forward<T>(on_call), std::forward<D>(on_drop))) {}

private:
typedef ZC_RETVAL (*CALL)(ZCPP_PARAM&& pvalue);
Expand All @@ -300,7 +267,7 @@ class ClosureMoveParam : public Owned<ZC_CLOSURE_TYPE> {
return on_call->func(ZCPP_PARAM(pvalue));
};
auto drop = [](void* ctx) { delete static_cast<Call*>(ctx); };
return {context, call, nullptr};
return {context, call, drop};
}

template <typename T>
Expand Down
240 changes: 240 additions & 0 deletions tests/universal/closure_const_ref_param.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
//
// Copyright (c) 2022 ZettaScale Technology
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
//
// Contributors:
// ZettaScale Zenoh Team, <zenoh@zettascale.tech>
//
#include "zenoh.hxx"
using namespace zenoh;

#undef NDEBUG
#include <assert.h>

//
// Test for all variants of construnctng 'ClosureConstRefParam' closures
//
// - from function
// - from object copy
// - from object reference
// - from object rvalue reference
// - from lambda expression
//

size_t callcnt = 1;
size_t dropcnt = 1;

Query query(::z_query_t{nullptr});

void show_primes(size_t v) {
std::cout << v << " = 1";
size_t d = 2;
while (v > 1) {
if (v % d == 0) {
std::cout << " * " << d;
v /= d;
} else {
d++;
}
}
std::cout << std::endl;
}

void on_reply_2(const Query&) { callcnt *= 2; };
void on_reply_3(const Query&) { callcnt *= 3; };
void on_reply_5(const Query&) { callcnt *= 5; };
void on_reply_7(const Query&) { callcnt *= 7; };
void on_reply_11(const Query&) { callcnt *= 11; };

struct OnCall {
OnCall() = default;
OnCall(const OnCall&) = delete;
OnCall(OnCall&&) = default;
OnCall& operator=(const OnCall&) = delete;
OnCall& operator=(OnCall&&) = default;

OnCall(int _v) : v(_v) {}
void operator()(const Query&) { callcnt *= v; };
int v;
};

void test_call() {
ClosureQuery f(on_reply_3);
ClosureQuery o(OnCall(5));
OnCall o7(7);
ClosureQuery r(o7);
OnCall o11(11);
ClosureQuery m(std::move(o11));
ClosureQuery l([](Query&&) { callcnt *= 13; });

// rvalue parameter tests
callcnt = 1;
f(query);
o(query);
r(query);
m(query);
l(query);

assert(callcnt == size_t(1) * 3 * 5 * 7 * 11 * 13);
}

void on_drop_2() { dropcnt *= 2; };
void on_drop_3() { dropcnt *= 3; };
void on_drop_5() { dropcnt *= 5; };

void on_drop_17() { dropcnt *= 17; };

struct OnDrop {
OnDrop(int _v) : v(_v) {}
void operator()() { dropcnt *= v; };
int v;
};

void test_call_f_drop() {
callcnt = 1;
dropcnt = 1;
{
ClosureQuery ff(on_reply_2, on_drop_2);
ClosureQuery fo(on_reply_3, OnDrop(3));
OnDrop d5(5);
ClosureQuery fr(on_reply_5, d5);
OnDrop d7(7);
ClosureQuery fm(on_reply_7, std::move(d7));
ClosureQuery fl(on_reply_11, []() { dropcnt *= 11; });

ff(query);
fo(query);
fr(query);
fm(query);
fl(query);

assert(dropcnt == size_t(1));
assert(callcnt == size_t(1) * 2 * 3 * 5 * 7 * 11);
}

assert(dropcnt == size_t(1) * 2 * 3 * 5 * 7 * 11);
}

void test_call_o_drop() {
callcnt = 1;
dropcnt = 1;
{
ClosureQuery of(OnCall(2), on_drop_2);
ClosureQuery oo(OnCall(3), OnDrop(3));
OnDrop d5(5);
ClosureQuery or_(OnCall(5), d5);
OnDrop d7(7);
ClosureQuery om(OnCall(7), std::move(d7));
ClosureQuery ol(OnCall(11), []() { dropcnt *= 11; });

of(query);
oo(query);
or_(query);
om(query);
ol(query);

assert(dropcnt == size_t(1));
assert(callcnt == size_t(1) * 2 * 3 * 5 * 7 * 11);
}

assert(dropcnt == size_t(1) * 2 * 3 * 5 * 7 * 11);
}

void test_call_r_drop() {
callcnt = 1;
dropcnt = 1;
{
OnCall f2(2);
OnCall f3(3);
OnCall f5(5);
OnCall f7(7);
OnCall f11(11);

ClosureQuery rf(f2, on_drop_2);
ClosureQuery ro(f3, OnDrop(3));
OnDrop d5(5);
ClosureQuery rr(f5, d5);
OnDrop d7(7);
ClosureQuery rm(f7, std::move(d7));
ClosureQuery rl(f11, []() { dropcnt *= 11; });

rf(query);
ro(query);
rr(query);
rm(query);
rl(query);

assert(dropcnt == size_t(1));
assert(callcnt == size_t(1) * 2 * 3 * 5 * 7 * 11);
}
assert(dropcnt == size_t(1) * 2 * 3 * 5 * 7 * 11);
}

void test_call_m_drop() {
callcnt = 1;
dropcnt = 1;
{
OnCall f2(2);
OnCall f3(3);
OnCall f5(5);
OnCall f7(7);
OnCall f11(11);

ClosureQuery mf(std::move(f2), on_drop_2);
ClosureQuery mo(std::move(f3), OnDrop(3));
OnDrop d5(5);
ClosureQuery mr(std::move(f5), d5);
OnDrop d7(7);
ClosureQuery mm(std::move(f7), std::move(d7));
ClosureQuery ml(std::move(f11), []() { dropcnt *= 11; });

mf(query);
mo(query);
mr(query);
mm(query);
ml(query);

assert(dropcnt == size_t(1));
assert(callcnt == size_t(1) * 2 * 3 * 5 * 7 * 11);
}
assert(dropcnt == size_t(1) * 2 * 3 * 5 * 7 * 11);
}

void test_call_l_drop() {
callcnt = 1;
dropcnt = 1;
{
ClosureQuery lf([](Query&&) { callcnt *= 2; }, on_drop_2);
ClosureQuery lo([](Query&&) { callcnt *= 3; }, OnDrop(3));
OnDrop d5(5);
ClosureQuery lr([](Query&&) { callcnt *= 5; }, d5);
OnDrop d7(7);
ClosureQuery lm([](Query&&) { callcnt *= 7; }, std::move(d7));
ClosureQuery ll([](Query&&) { callcnt *= 11; }, []() { dropcnt *= 11; });

lf(query);
lo(query);
lr(query);
lm(query);
ll(query);

assert(dropcnt == size_t(1));
assert(callcnt == size_t(1) * 2 * 3 * 5 * 7 * 11);
}
assert(dropcnt == size_t(1) * 2 * 3 * 5 * 7 * 11);
}

int main(int argc, char** argv) {
test_call();
test_call_f_drop();
test_call_o_drop();
test_call_r_drop();
test_call_m_drop();
test_call_l_drop();
}

0 comments on commit 91f829e

Please sign in to comment.