mirror of
https://github.com/genodelabs/genode.git
synced 2025-04-19 08:36:49 +00:00
parent
fe18db4d34
commit
69e8e9f3f1
repos
base
include/util
recipes
src/test/callable
gems/run
95
repos/base/include/util/callable.h
Normal file
95
repos/base/include/util/callable.h
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* \brief Utility for passing lambda arguments to non-template functions
|
||||
* \author Norman Feske
|
||||
* \date 2025-01-14
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2025 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE__UTIL__CALLABLE_H_
|
||||
#define _INCLUDE__UTIL__CALLABLE_H_
|
||||
|
||||
#include <util/interface.h>
|
||||
|
||||
namespace Genode { template <typename, typename...> struct Callable; };
|
||||
|
||||
|
||||
/**
|
||||
* Utility for passing lambda arguments to non-template functions
|
||||
*
|
||||
* A function or method taking a lambda as argument must always be a template
|
||||
* because the captured state is known only at the caller site. For example,
|
||||
* within a display driver, the following function is meant to call 'fn' for
|
||||
* each 'Connector const &' that is currently known.
|
||||
*
|
||||
* ! void for_each_connector(auto const &fn);
|
||||
*
|
||||
* The type of 'fn' as template parameter stands in the way in two situations.
|
||||
* First, 'for_each_connector' must be implemented in a header because it is a
|
||||
* template. But its inner working might be complex and better be hidden inside
|
||||
* a compilation unit. Second, 'for_each_connector' cannot be part of an
|
||||
* abstract interface because template methods cannot be virtual functions.
|
||||
*
|
||||
* The 'Callable' utility addresses both situations by introducing an abstract
|
||||
* function type 'Ft' for the plain signature (arguments, return type) of a
|
||||
* lambda, and an 'Fn' type implementing the abstract function for a concrete
|
||||
* lambda argument. E.g., the following 'With_connector' type defines a
|
||||
* signature for a lambda that takes a 'Connector const &' argument.
|
||||
*
|
||||
* ! With_connector = Callable<void, Connector const &>;
|
||||
*
|
||||
* The 'With_connector' definition now allows for defining a pure virtual
|
||||
* function by referring to the signature's function type 'Ft'. It is a good
|
||||
* practice to use a '_' prefix because this method is not meant to be the API.
|
||||
*
|
||||
* ! virtual void _for_each_connector(With_connector::Ft const &) = 0;
|
||||
*
|
||||
* The user-facing API should best accept a regular 'auto const &fn' argument
|
||||
* and call the virtual function with a concrete implementation of the function
|
||||
* type, which is accomplished via the 'Fn' type.
|
||||
*
|
||||
* ! void for_each_connector(auto const &fn)
|
||||
* ! {
|
||||
* ! _for_each_connector( With_connector::Fn { fn } );
|
||||
* ! }
|
||||
*
|
||||
* At the implementation site of '_for_each_connector', the 'Ft' argument
|
||||
* can be called like a regular lambda argument. At the caller site,
|
||||
* 'for_each_connector' accepts a regular lambda argument naturally expecting
|
||||
* a 'Connector const &'.
|
||||
*/
|
||||
template <typename RET, typename... ARGS>
|
||||
struct Genode::Callable
|
||||
{
|
||||
struct Ft : Interface { virtual RET operator () (ARGS &&...) const = 0; };
|
||||
|
||||
template <typename FN>
|
||||
struct Fn : Ft
|
||||
{
|
||||
FN const &_fn;
|
||||
Fn(FN const &fn) : _fn(fn) { };
|
||||
RET operator () (ARGS &&... args) const override { return _fn(args...); }
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
template <typename... ARGS>
|
||||
struct Genode::Callable<void, ARGS...>
|
||||
{
|
||||
struct Ft : Interface { virtual void operator () (ARGS &&...) const = 0; };
|
||||
|
||||
template <typename FN>
|
||||
struct Fn : Ft
|
||||
{
|
||||
FN const &_fn;
|
||||
Fn(FN const &fn) : _fn(fn) { };
|
||||
void operator () (ARGS &&... args) const override { _fn(args...); }
|
||||
};
|
||||
};
|
||||
|
||||
#endif /* _INCLUDE__UTIL__CALLABLE_H_ */
|
1
repos/base/recipes/pkg/test-callable/README
Normal file
1
repos/base/recipes/pkg/test-callable/README
Normal file
@ -0,0 +1 @@
|
||||
Scenario that tests the util/callable.h utility
|
1
repos/base/recipes/pkg/test-callable/archives
Normal file
1
repos/base/recipes/pkg/test-callable/archives
Normal file
@ -0,0 +1 @@
|
||||
_/src/test-callable
|
1
repos/base/recipes/pkg/test-callable/hash
Normal file
1
repos/base/recipes/pkg/test-callable/hash
Normal file
@ -0,0 +1 @@
|
||||
2025-01-14 74d8426380b0552c3c26487469c0aeeec32cbde0
|
17
repos/base/recipes/pkg/test-callable/runtime
Normal file
17
repos/base/recipes/pkg/test-callable/runtime
Normal file
@ -0,0 +1,17 @@
|
||||
<runtime ram="32M" caps="1000" binary="test-callable">
|
||||
|
||||
<fail after_seconds="20"/>
|
||||
<succeed>
|
||||
[init] result of action.compute: 34
|
||||
[init] accessing XML node, state=reset
|
||||
[init] --- finished callable test ---
|
||||
</succeed>
|
||||
|
||||
<content>
|
||||
<rom label="ld.lib.so"/>
|
||||
<rom label="test-callable"/>
|
||||
</content>
|
||||
|
||||
<config/>
|
||||
|
||||
</runtime>
|
2
repos/base/recipes/src/test-callable/content.mk
Normal file
2
repos/base/recipes/src/test-callable/content.mk
Normal file
@ -0,0 +1,2 @@
|
||||
SRC_DIR = src/test/callable
|
||||
include $(GENODE_DIR)/repos/base/recipes/src/content.inc
|
1
repos/base/recipes/src/test-callable/hash
Normal file
1
repos/base/recipes/src/test-callable/hash
Normal file
@ -0,0 +1 @@
|
||||
2025-01-14 86e453af4b2c1696028b76af2d459b3891550f11
|
1
repos/base/recipes/src/test-callable/used_apis
Normal file
1
repos/base/recipes/src/test-callable/used_apis
Normal file
@ -0,0 +1 @@
|
||||
base
|
78
repos/base/src/test/callable/main.cc
Normal file
78
repos/base/src/test/callable/main.cc
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* \brief Test for the Callable utility
|
||||
* \author Norman Feske
|
||||
* \date 2025-01-14
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2025 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#include <base/log.h>
|
||||
#include <base/component.h>
|
||||
#include <util/callable.h>
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
struct Action : Interface
|
||||
{
|
||||
/*
|
||||
* A functor argument taking 3 ints and returning one int.
|
||||
*/
|
||||
using With_3_numbers = Callable<int, int, int, int>;
|
||||
|
||||
virtual int _compute(With_3_numbers::Ft const &) const = 0;
|
||||
|
||||
int compute(auto const &fn) const { return _compute( With_3_numbers::Fn { fn } ); }
|
||||
|
||||
|
||||
/*
|
||||
* A functor argument taking an Xml_node const &, without return value
|
||||
*/
|
||||
using With_xml_node = Callable<void, Xml_node const &>;
|
||||
|
||||
virtual void _with_xml(With_xml_node::Ft const &) = 0;
|
||||
|
||||
void with_xml(auto const &fn) { _with_xml( With_xml_node::Fn { fn } ); }
|
||||
};
|
||||
|
||||
|
||||
static void test(Action &action)
|
||||
{
|
||||
int const result = action.compute([&] (int a, int b, int c) {
|
||||
return a + b + c; });
|
||||
|
||||
log("result of action.compute: ", result);
|
||||
|
||||
action.with_xml([&] (Xml_node const &node) {
|
||||
log("accessing XML node, state=",
|
||||
node.attribute_value("state", String<16>())); });
|
||||
}
|
||||
|
||||
|
||||
void Component::construct(Env &)
|
||||
{
|
||||
log("--- callable test ---");
|
||||
|
||||
struct Test_action : Action
|
||||
{
|
||||
int _compute(With_3_numbers::Ft const &fn) const override
|
||||
{
|
||||
return fn(10, 11, 13);
|
||||
}
|
||||
|
||||
void _with_xml(With_xml_node::Ft const &fn) override
|
||||
{
|
||||
Xml_node const node { "<power state=\"reset\"/>" };
|
||||
fn(node);
|
||||
}
|
||||
} action { };
|
||||
|
||||
test(action);
|
||||
|
||||
log("--- finished callable test ---");
|
||||
}
|
3
repos/base/src/test/callable/target.mk
Normal file
3
repos/base/src/test/callable/target.mk
Normal file
@ -0,0 +1,3 @@
|
||||
TARGET = test-callable
|
||||
SRC_CC = main.cc
|
||||
LIBS = base
|
@ -653,6 +653,7 @@ set default_test_pkgs {
|
||||
test-spark_secondary_stack
|
||||
test-alarm
|
||||
test-black_hole
|
||||
test-callable
|
||||
test-clipboard
|
||||
test-depot_query_index
|
||||
test-ds_ownership
|
||||
|
Loading…
x
Reference in New Issue
Block a user