Skip to content
This repository was archived by the owner on Jun 1, 2022. It is now read-only.
Draft
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
8 changes: 4 additions & 4 deletions protos/bento/protos/graph.proto
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ message Node {
/* Component access, mutation */
// Retrieve - node that retrieves a value from an entity's component
message Retrieve {
// Reference to the attribute to retrieve
AttributeRef retrieve_attr = 1;
// Reference to the value to retrieve
ValueRef retrieve_value = 1;
}

// Mutate - node mutate the value in an entity's component based
Expand All @@ -33,8 +33,8 @@ message Node {
// attribute's value to
Node to_node = 1;

// Reference to the attribute to mutate with value
AttributeRef mutate_attr = 2;
// Reference to the value to mutate
ValueRef mutate_ref = 2;
}

/* Control flow */
Expand Down
8 changes: 5 additions & 3 deletions protos/bento/protos/references.proto
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@
syntax = "proto3";
package bento.protos;

// Fully-qualified reference to an component's attribute
// Fully-qualified reference to a value stored in a component
// Assumes that the component is bound the entity and attribute exists in the
// component type
message AttributeRef {
// id of the entity to the retrieve from
message ValueRef {
// id of the entity to retrieve from
uint32 entity_id = 1;
// Name of the component type to retrieve from entity
string component = 2;
// Name of the attribute to retrieve from component
string attribute = 3;
// A list of indexes
repeated int64 attr_indexes = 4;
}
30 changes: 15 additions & 15 deletions protos/bento/protos/services.proto
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ service EngineService {
// Run one step of the simulation
rpc StepSimulation(StepSimulationReq) returns (StepSimulationResp);

// Set, Get component's Attributes
rpc GetAttribute(GetAttributeReq) returns (GetAttributeResp);
rpc SetAttribute(SetAttributeReq) returns (SetAttributeResp);
// Set, Get values in components
rpc GetValue(GetValueReq) returns (GetValueResp);
rpc SetValue(SetValueReq) returns (SetValueResp);
}

message GetVersionReq {}
Expand Down Expand Up @@ -72,23 +72,23 @@ message StepSimulationReq {
}
message StepSimulationResp {}

message GetAttributeReq {
// Name of the simulation to retrieve the attribute from
message GetValueReq {
// Name of the simulation to retrieve the value from
string sim_name = 1;
// Reference to the attribute to retrieve
AttributeRef attribute = 2;
// Reference to the value to retrieve
ValueRef value_ref = 2;
}
message GetAttributeResp {
// Stored value of the requested attribute
message GetValueResp {
// Stored value of the requested value
Value value = 1;
}

message SetAttributeReq {
// Name of the simulation to set the attribute
message SetValueReq {
// Name of the simulation to set the value
string sim_name = 1;
// Reference to the target attribute to set
AttributeRef attribute = 2;
// The value to the set the target attribute to
// Reference to the target value to set
ValueRef value_ref = 2;
// The value to the set the target ValueRef to
Value value = 3;
}
message SetAttributeResp {}
message SetValueResp {}
1 change: 1 addition & 0 deletions sim/lib/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ add_library(core-test
src/ics/util/composable.test.cpp
src/ics/util/setIntersection.test.cpp
src/ics/util/typeMap.test.cpp
src/optics/lens.test.cpp
)
target_link_libraries(core-test
PRIVATE core
Expand Down
179 changes: 179 additions & 0 deletions sim/lib/core/include/core/optics/lens.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
#ifndef BENTOBOX_LENS_HPP
#define BENTOBOX_LENS_HPP

#include <functional>
#include <type_traits>

namespace {
template <class... Args>
struct is_same_args {
// This static constexpr function compares the given type arguments and
// ensures that they are the same as Args
template <size_t N = 0, class... OtherArgs>
static constexpr bool _as() {
// constexpr is required here because N is a non-type template
// argument
if constexpr (N == sizeof...(OtherArgs)) {
return true;
} else if (std::is_same_v<
std::remove_reference_t<
std::tuple_element_t<N, std::tuple<OtherArgs...>>>,
std::remove_reference_t<
std::tuple_element_t<N, std::tuple<Args...>>>>) {
return _as<N + 1, OtherArgs...>();
} else {
return false;
}
}

// Alias to the recursive comparison constexpr function
template <class... OtherArgs>
static constexpr bool as() {
return _as<0, OtherArgs...>();
}
};
} // namespace

namespace optics {

// Note on std::move vs static_cast<std::remove_reference_t<T>&&>
// Later in the code, you will see that I have used static_cast instead of
// std::move. The reason is that we need to cast to an rvalue reference
// because that's the type of the function's parameter.
// std::move is not used because it is semantically incorrect, though it
// does the same thing.

template <class Return, class... Args>
class Getter {
protected:
public:
const std::function<Return(std::remove_reference_t<Args>&&...)> getter;
// A getter cannot be constructed without a getter function
Getter() = delete;
explicit Getter(std::function<Return(Args...)>&& getter)
: getter(std::move(getter)) {}
explicit Getter(const std::function<Return(Args...)>& getter)
: getter(getter) {}

template <class... RArgs>
requires(is_same_args<Args...>::template as<RArgs...>()) Return
get(RArgs&&... args) const {
return getter(static_cast<std::remove_reference_t<RArgs>>(args)...);
}

template <class NewReturn>
Getter<NewReturn, Args...> compose(
std::function<NewReturn(Return)>&& composeGetter) const {
// Make a copy of the getter so that if the current Getter is destroyed
// The returned getter is still usable
std::function<Return(Args...)> getterCopy(getter);
return Getter<NewReturn, Args...>(
[getterCopy, composeGetter](Args&&... args) {
return std::invoke(
composeGetter,
std::invoke(getterCopy, std::forward<Args>(args)...));
});
}

// Oddly, C++ does not recognise lambdas when std::function is used here.
// So, C++20 requirements are used.
// The attempted definition of a std::function is:
// template<class...NewArgs>
// Getter<Return, NewArgs...>
// precompose(std::function<std::tuple_element_t<0,
// std::tuple<Args...>>(NewArgs...)> precomposeGetter) {
template <class... NewArgs, class Fn>
// Require that the current getter only needs one argument
requires(sizeof...(Args) == 1) &&
// Require that the type Fn is something invocable with the NewArgs and
// returns the first argument
std::is_invocable_r_v<
std::tuple_element_t<0, std::tuple<Args...>>, Fn,
NewArgs...> Getter<Return,
NewArgs...> precompose(Fn&& precomposeGetter)
const {
// Make a copy of the getter so that if the current Getter is destroyed
// The returned getter is still usable
std::function<Return(Args...)> getterCopy(getter);
return Getter<Return, NewArgs...>(
[getterCopy, precomposeGetter](NewArgs&&... args) {
return std::invoke(getterCopy,
std::invoke(precomposeGetter,
std::forward<NewArgs>(args)...));
});
}
};

template <class Return, class... Args>
class Lens {
private:
typedef std::function<void(Return&, Return&&)> SetterFn;
// When writing the getter as a lambda, it is important to specify the
// arguments as rvalue references and the return type as an lvalue
// reference.
typedef std::function<Return&(Args&&...)> GetterFn;

public:
const SetterFn setter;
const GetterFn getter;
// A lens cannot be constructed without any functions
Lens() = delete;
Lens(GetterFn&& getter, SetterFn&& setter)
: getter(std::move(getter)), setter(std::move(setter)) {}
Lens(const GetterFn& getter, const SetterFn& setter)
: getter(getter), setter(setter) {}

template <class... RArgs>
requires(is_same_args<Args...>::template as<RArgs...>()) Return& get(
RArgs&&... args) const {
return getter(static_cast<std::remove_reference_t<RArgs>&&>(args)...);
}

template <class RReturn, class... RArgs>
// Ensure that the arguments are the correct type
requires(is_same_args<Args...>::template as<RArgs...>())
// Ensure that RReturn is the correct type
&& std::is_same_v<std::remove_reference_t<RReturn>, Return> void set(
RReturn&& val, RArgs&&... args) const {
auto& ref = get(std::forward<RArgs>(args)...);
this->setter(ref, static_cast<std::remove_reference_t<RReturn>&&>(val));
}

template <class NewReturn>
Lens<NewReturn, Args...> compose(
const Lens<NewReturn, Return>& otherLens) const {
std::function<NewReturn&(Args && ...)> get =
[
getter = this->getter, otherGetter = otherLens.getter
]<class... PArgs>(PArgs && ... args)
->NewReturn& {
return otherGetter(
static_cast<Return&&>(getter(std::forward<PArgs>(args)...)));
};

return Lens<NewReturn, Args...>(get, otherLens.setter);
}

template <class FirstArg, class... NewArgs>
requires(sizeof...(Args) == 1) &&
std::is_same_v<
std::tuple_element_t<0, std::tuple<Args...>>,
FirstArg> Lens<Return, NewArgs...> precompose(const Lens<FirstArg,
NewArgs...>
otherLens) const {
std::function<Return&(NewArgs && ...)> get =
[
getter = this->getter, otherGetter = otherLens.getter
]<class... PArgs>(PArgs && ... args)
->Return& {
return getter(static_cast<FirstArg&&>(
otherGetter(std::forward<PArgs>(args)...)));
};

return Lens<Return, NewArgs...>(get, this->setter);
}
};

} // namespace optics

#endif // BENTOBOX_LENS_HPP
Loading