Suppose I have an immutable wrapper:
template<class T>
struct immut {
T const& get() const {return *state;}
immut modify( std::function<T(T)> f ) const { return immut{f(*state)}; }
immut(T in):state(std::make_shared<T>(std::move(in))){}
private:
std::shared_ptr<T const> state;
};
if I have an immut<Bob> b, I can turn a Bob(Bob) operation into something that can replace my b.
template<class T>
std::function<immut<T>(immut<T>)> on_immut( std::function<void(T&)> f ){
return [=](auto&&in){ return in.modify( [&](auto t){ f(t); return t; } ); };
}
So if Bob is int x,y;, I can turn naive mutable code [](auto& b){ b.x++; } into a immut<Bob> updater.
Now, what if Bob in turn has immut<Alice> members, which in turn have immut<Charlie> members.
Suppose I have a Charlie updater, void(Charlie&). And I know where the Charlie I want to update is. In mutable land it would look like :
void update( Bob& b ){
modify_charlie(b.a.c[77]);
}
and I might split it into:
template<class S, class M>
using get=std::function<M&(S&)>;
template<class X>
using update=std::function<void(X&)>;
template<class X>
using produce=std::function<X&()>;
void update( produce<Bob> b, get<Bob, Alice> a, get<Alice, Charlie> c, update<Charlie> u ){
u(c(a(b())));
}
or even go algebraic and have (pseudo code):
get<A,C> operator|(get<A,B>,get<B,C>);
update<A> operator|(get<A,B>,update<B>);
produce<B> operator|(produce<A>,get<A,B>);
void operator|(produce<A>, update<A>);
letting us chain together operations as needed.
void update( produce<Bob> b, get<Bob, Alice> a, get<Alice, Charlie> c, update<Charlie> u ){std::
u(c(a(b())));
}
becomes
b|a|c|u;
where the steps can be stitched together where-ever they need to be.
What is the equivalent with immuts, and is there a name for it? Ideally I want the steps to be as isolated yet as composable as in the mutable case, with the naive "leaf code" being on mutable state structs.
What is the equivalent with immuts, and is there a name for it?
IDK whether there is a universal name for sub-state manipulation. But in Haskell, there is a Lens which offers exactly what you want.
In C++, you can consider a lens as a pair of getter and setter functions, both of which only focus on its direct subpart. Then there is a composer to combine two lens together to focus on a deeper subpart of a structure.
// lens for `A` field in `D`
template<class D, class A>
using get = std::function<A const &(D const &)>;
template<class D, class A>
using set = std::function<D(D const &, A)>;
template<class D, class A>
using lens = std::pair<get<D, A>, set<D, A>>;
// compose (D, A) lens with an inner (A, B) lens,
// return a (D, B) lens
template<class D, class A, class B>
lens<D, B>
lens_composer(lens<D, A> da, lens<A, B> ab) {
auto abgetter = ab.first;
auto absetter = ab.second;
auto dagetter = da.first;
auto dasetter = da.second;
get<D, B> getter = [abgetter, dagetter]
(D const &d) -> B const&
{
return abgetter(dagetter(d));
};
set<D, B> setter = [dagetter, absetter, dasetter]
(D const &d, B newb) -> D
{
A const &a = dagetter(d);
A newa = absetter(a, newb);
return dasetter(d, newa);
};
return {getter, setter};
};
You can write a basic lens like this:
struct Bob {
immut<Alice> alice;
immut<Anna> anna;
};
auto bob_alice_lens
= lens<Bob, Alice> {
[] (Bob const& b) -> Alice const & {
return b.alice.get();
},
[] (Bob const& b, Alice newAlice) -> Bob {
return { immut{newAlice}, b.anna };
}
};
Note this process can be automated by macro.
Then if Bob contains immut<Alice>, Alice contains immut<Charlie>, you can write 2 lens (Bob to Alice and Alice to Charlie), the Bob to Charlie lens can be composed by:
auto bob_charlie_lens =
lens_composer(bob_alice_lens, alice_charlie_lens);
Live Demo
NOTE:
Below is a more complete example with linear memory growth w.r.t depth, without type-erasure overhead (std::function), and with full compile time type check. It also uses macro to generate basic lens.
#include <type_traits>
#include <utility>
template<class T>
using GetFromType = typename T::FromType;
template<class T>
using GetToType = typename T::ToType;
// `get` and `set` are fundamental operations for Lens,
// this Mixin will add utilities based on `get` and `set`
template<class Derived>
struct LensMixin {
Derived &self() { return static_cast<Derived&>(*this); }
// f has type: A& -> void
template<class D, class Mutation>
auto modify(D const &d, Mutation f)
{
auto a = self().get(d);
f(a);
return self().set(d, a);
}
};
template<
class Getter, class Setter,
class D, class A
>
struct SimpleLens : LensMixin<SimpleLens<Getter, Setter, D, A>> {
static_assert(std::is_same<
std::invoke_result_t<Getter, const D&>, const A&>{},
"Getter should return const A& for (const D&)");
static_assert(std::is_same<
std::invoke_result_t<Setter, const D&, A>, D>{},
"Setter should return D for (const D&, A)");
using FromType = D;
using ToType = A;
SimpleLens(Getter getter, Setter setter)
: getter(getter)
, setter(setter)
{}
A const &get(D const &d) { return getter(d); }
D set(D const &d, A newa) { return setter(d, newa); }
private:
Getter getter;
Setter setter;
};
template<
class LensDA, class LensAB
>
struct ComposedLens : LensMixin<ComposedLens<LensDA, LensAB>> {
static_assert(std::is_same<
GetToType<LensDA>, GetFromType<LensAB>
>{}, "Cannot compose two Lens with wrong intermediate type");
using FromType = GetFromType<LensDA>;
using ToType = GetToType<LensAB>;
private:
using intermediateType = GetToType<LensDA>;
using D = FromType;
using B = ToType;
LensDA da;
LensAB ab;
public:
ComposedLens(LensDA da, LensAB ab) : da(da), ab(ab) {}
B const &get(D const &d) { return ab.get(da.get(d)); }
D set(D const &d, B newb) {
const auto &a = da.get(d);
auto newa = ab.set(a, newb);
return da.set(d, newa);
}
};
namespace detail {
template<class LensDA, class LensAB>
auto MakeComposedLens(LensDA da, LensAB ab) {
return ComposedLens<LensDA, LensAB> { da, ab };
}
template<class D, class A, class Getter, class Setter>
auto MakeSimpleLens(Getter getter, Setter setter)
{
return SimpleLens<Getter, Setter, D, A> {
getter, setter
};
}
}
template<class LensDA, class LensAB>
auto lens_composer(LensDA da, LensAB ab) {
return detail::MakeComposedLens (da, ab);
}
#include <memory>
template<class T>
struct immut {
T const& get() const {return *state;}
immut(T in):state(std::make_shared<T>(std::move(in))){}
private:
std::shared_ptr<T const> state;
};
#define MAKE_SIMPLE_LENS(D, A, Aname) \
detail::MakeSimpleLens<D, A>( \
+[] (D const &d) -> A const & { \
return d . Aname . get(); \
}, \
+[] (D const &d, A newa) -> D { \
D newd = d; \
newd . Aname = newa; \
return newd; \
})
struct Charlie {
int id = 0;
};
struct Alice{
immut<Charlie> charlie;
};
struct Anna {};
struct Bob {
immut<Alice> alice;
immut<Anna> anna;
};
auto alice_charlie_lens = MAKE_SIMPLE_LENS(Alice, Charlie, charlie);
auto bob_alice_lens = MAKE_SIMPLE_LENS(Bob, Alice, alice);
auto bob_charlie_lens = lens_composer(bob_alice_lens, alice_charlie_lens);
static_assert(std::is_same<GetFromType<decltype(bob_charlie_lens)>, Bob>{});
static_assert(std::is_same<GetToType<decltype(bob_charlie_lens)>, Charlie>{});
#include <iostream>
int main() {
immut<Charlie> charlie{Charlie{77}};
immut<Alice> alice{Alice{charlie}};
immut<Anna> anna{Anna{}};
Bob bob{alice, anna};
std::cout << "bob -> anna: " << static_cast<void const*>(&bob.anna.get()) << "\n";
std::cout << "bob -> charlie: " << bob_charlie_lens.get(bob).id << "\n";
// Bob newbob = bob_charlie_lens.set(bob, Charlie{148});
Bob newbob = bob_charlie_lens.modify(bob, [] (auto &charlie) {
charlie.id += (148 - 77);
});
std::cout << "new bob -> anna: " << static_cast<void const*>(&bob.anna.get()) << "\n";
std::cout << "old bob -> charlie: " << bob_charlie_lens.get(bob).id << "\n";
std::cout << "new bob -> charlie: " << bob_charlie_lens.get(newbob).id << "\n";
}
This does not work:
struct Type {
virtual bool func(const std::string& val) const noexcept = 0;
}
// in main
optional<Type> = some_function_returning_optional_type();
and fails with a error message:
error: cannot declare field 'std::experimental::fundamentals_v1::_Optional_base<Type, false>::<anonymous union>::_M_payload' to be of abstract type 'Type'
Changing the Type to have a non-pure function works, but is not appropriate in this case, because there cannot be an instance of Type in my code, only classes which inherit from it should be able to exist.
std::optional<T> stores its value in-place - it therefore needs to know the size of T to work correctly, and T must be a concrete type that can be instantiated. You can think of std::optional<T> as:
template <typename T>
struct optional
{
std::aligned_storage_t<sizeof(T), alignof(T)> _data;
bool _set;
};
An abstract type represents an interface - polymorphism and some sort of indirection are required to work with abstract types. std::optional doesn't have any indirection by design.
Your proposal of optional will of course work but it would offend me to have to write
x.value()->do_something();
and I'd be concerned that users might do something daft:
x.value().reset(); // now what?
We can achieve polymorphism with a non-polymorphic interface by using a wrapper.
Here's one way:
#include <optional>
#include <iostream>
// the Foo interface/base class
struct Foo
{
virtual ~Foo() = default;
virtual Foo* clone() const { return new Foo(*this); }
virtual void do_something() {
std::cout << "something Fooey\n";
}
};
// a service for managing Foo and classes derived from Foo
struct FooService
{
template<class Arg>
Foo* clone(Arg&& arg)
{
using d_type = std::decay_t<Arg>;
return new d_type(std::forward<Arg>(arg));
}
template<class Arg>
Foo* clone(Foo* arg)
{
return arg->clone();
}
Foo* release(Foo*& other) noexcept
{
auto tmp = other;
other = nullptr;
return tmp;
}
};
// implement the Foo interface in terms of a pimpl
template<class Holder>
struct BasicFoo
{
decltype(auto) do_something() {
return get().do_something();
}
private:
Foo& get() noexcept { return static_cast<Holder*>(this)->get_impl(); }
Foo const& get() const noexcept { return static_cast<Holder const*>(this)->get_impl(); }
};
// a type for holding anything derived from a Foo
// can be initialised by anything Foo-like and handles copy/move correctly
struct FooHolder : BasicFoo<FooHolder>
{
template
<
class Arg,
std::enable_if_t
<
std::is_base_of_v<Foo, std::decay_t<Arg>>
>* = nullptr
>
FooHolder(Arg&& arg)
: service_()
, ptr_(service_.clone(std::forward<Arg>(arg)))
{}
FooHolder(FooHolder const& other)
: service_()
, ptr_(other.ptr_->clone())
{
}
FooHolder(FooHolder && other) noexcept
: service_()
, ptr_(service_.release(other.ptr_))
{
}
FooHolder& operator=(FooHolder const& other)
{
auto tmp = other;
std::swap(ptr_, tmp.ptr_);
return *this;
}
FooHolder& operator=(FooHolder && other) noexcept
{
auto tmp = std::move(other);
std::swap(ptr_, tmp.ptr_);
return *this;
}
~FooHolder()
{
delete ptr_;
}
Foo& get_impl() noexcept { return *ptr_; }
Foo const& get_impl() const noexcept { return *ptr_; }
FooService service_;
Foo* ptr_;
};
// now we can supply as many overrides of Foo as we like
struct Bar : Foo
{
virtual Foo* clone() const { return FooService().clone(*this); }
virtual void do_something() {
std::cout << "something Barey\n";
}
};
int main()
{
std::optional<FooHolder> opt;
// note that we're initialising cleanly
opt = Bar {};
// and we don't expose the pointer so the user can't
// destroy the pimpl accidentally
opt.value().do_something();
}
In C++, we can pass a function/functor to a function like so:
template <typename F>
void doOperation(int a, int b, F f){
std::cout << "Result: " << f(a,b) << std::endl;
}
we can then use both functions and functors:
int add(const int &a, const int &b){ return a+b; }
struct subtract(){
void operator() (const int &a, const int &b) { return a-b; }
};
and use it in the following manner:
doOperation(1,2,add);
doOperation(5,2,subtract());
My question is, can I do something similar with a class and pass a function as an argument to a class, store it and use it later? E.g.
template <typename F>
class doOperation{
public:
doOperation(int &a, int &b, F f) : a(a), b(b), f(f) {};
void setOperands(int &a, int &b) { this->a = a; this->b = b };
void performCalculation(){
std::cout << "Result: " << f(a,b) << std::endl;
}
private:
int a,b;
F f;
}
So that we may assign it a function once and then later use it:
doOperation summing(1,2,add);
summing.setOperands(2,3);
summing.performCalculation();
doOperation subtraction(7,3,subtract());
subtraction.performCalculation();
If my examples are valid, I would appreciate the explanation for the mechanics here as I seem to be getting a bit lost. In case I missed something, I am looking for hints on whether this can be achieved.
Lastly, how would I then use such a class doOperation in other functions and classes. For example, would defining something like this inside a member function require me to template the new class, its member function, and how would it be declared and used:
class higherFunctionality{
public:
higherFunctionality() {...}
void coolThings(){
doOperation *myOperation = operationFactory( ... );
myOperation->setOperands(4,5);
myOperation->performCalculation();
}
};
Yes, but you have to supply the type when you instantiate template classes. The usual way to deal with this is to create a helper function:
template < typename Fun > struct operation_class
{
operation_class(Fun f) : fun{f} {}
Fun fun;
};
template < typename Fun >
operation_class<Fun> operation(Fun fun) { return operation_class<Fun>{fun}; }
int main()
{
auto op0 = operation(some_fun);
auto op1 = operation(some_functor{});
}
Frankly though, you are better off just using lambda:
auto op0 = [a,b]() { return sum(a,b); };
auto op1 = [a,b]() { return subtract{a,b}(); }
// C++17:
auto op2 = [op=subtract{a,b}] { return op(); };
I'm building an Entity-Component system using template metaprogramming. I keep getting either Cannot convert from [base type] to [type user requested]& or Cannot convert NullComponent to [type user requested]& errors:
class Entity {
public:
Entity() = default;
~Entity() = default;
template<typename C, typename... Args>
void AddComponent(Args&&... args);
template<typename C>
C& GetComponent();
protected:
private:
//...add/get helper methods here...
unsigned int _id;
std::vector<std::unique_ptr<IComponent>> _components;
};
template<typename C>
C& Entity::GetComponent() {
for(auto c : _components) {
if(std::is_base_of<a2de::IComponent&, C&>().value && std::is_same<decltype(c), C&>().value) {
return *c; //<-- error here
}
}
return NullComponent(); //<-- and here
}
EDIT
These options seem to work for now.
template<typename C>
const C& Entity::GetComponent() const {
for(auto& uc : _components) {
auto* c = dynamic_cast<C*>(uc.get());
if(c && std::is_base_of<a2de::IComponent&, C&>().value && std::is_same<decltype(c), C&>().value) {
return *c;
}
}
throw std::runtime_error(std::string("Component not available."));
}
OR
class Entity {
public:
//same as before...
protected:
private:
//same as before...
a2de::NullComponent _null_component;
};
template<typename C>
const C& Entity::GetComponent() const {
for(auto& uc : _components) {
auto* c = dynamic_cast<C*>(uc.get());
if(c && std::is_base_of<a2de::IComponent&, C&>().value && std::is_same<decltype(c), C&>().value) {
return *c;
}
}
return _null_component;
}
At least three things:
In GetComponent() you iterate over unique_ptr elements and compare their type (always std::unique_ptr<IComponent>) with something else in the std::is_same. You probably don't want that.
You are returning a reference to a temporary in the final return, it seems.
return *c needs a dynamic_cast unless C == IComponent.
EDIT
Also:
std::is_base_of makes no sense with references. Even with class NullComponent : IComponent {};, you would still get std::is_base_of<IComponent&, NullComponent&>::value == false.
And you do not check for nullptr
In the end, it seems to me that you should replace your for loop with
for(auto& component : _components) {
auto* c = dynamic_cast<C*>(component.get());
if (c)
{
return *c;
}
}
At a high level, from what I can figure out, the return type cannot be used to define the template type. The parameter list can be used to define the template type.
So, for example, this might work -
template<typename C>
void Entity::GetComponent(C *obj) {
for(auto c : _components) {
if(std::is_base_of<a2de::IComponent&, C&>().value && std::is_same<decltype(c), C&>().value) {
obj = c; //<-- error here
return;
}
}
obj = NULL;
return; //<-- and here
}
Hope this helps.
Is there a way to prevent shared_from_this() call for a stack-allocated object ?
The enable_shared_from_this<> in the base classes list is a strong indicator for class user, but is there a way to enforce the correct usage ?
Example code:
class C : public enable_shared_from_this<C>
{
public:
shared_ptr<C> method() { return shared_from_this(); }
};
void func()
{
C c;
shared_ptr<C> ptr = c.method(); // exception coming from shared_from_this()
}
So to protect against this problem you can make your constructors private and only provide creation functions that return shared_ptr - this way the object can't be allocated on the stack, like this:
class C : public enable_shared_from_this<C>
{
public:
static shared_ptr<C> create() { return shared_ptr<C>(new C() ); }
shared_ptr<C> method() { shared_from_this(); }
private:
C() {...}
// Make operator= and C(const C&) private unimplemented
// so the used cant do bad things like C c( * c_ptr );
C& operator=( const C & );
C( const C & );
};
void func()
{
C c; // This doesn't compile
shared_ptr<C> ptr = c.method(); // So you can never get this
}
void altfunc()
{
shared_ptr<C> c_ptr = C::create();
C & c_ref = *c;
shared_ptr<C> ptr = c_ref.method(); // OK
}
If you find yourself wishing for an operator= you can provide a clone function using a private implemented copy constructor, something like this
// This goes in class C
shared_ptr<C> C::clone() const
{
return shared_ptr<C>( new C(*this) );
}
// This is how you can use it
shared_ptr<C> c2 = c1->clone();
I found the solution.
In Dune library they use stack-compatible enable_shared_from_this class template adaptation.
Check the original source code here or check my example with the quick copy&paste & cleanup of code:
#include <cstdio>
#include <cassert>
#include <memory>
#include <iostream>
template<class T>
struct null_deleter
{
void operator() (T*) const {}
};
template<typename T>
inline std::shared_ptr<T> stackobject_to_shared_ptr(T & t)
{
return std::shared_ptr<T>(&t, null_deleter<T>());
}
template<typename T, typename T2>
inline std::shared_ptr<T2> stackobject_to_shared_ptr(T & t)
{
return std::shared_ptr<T2>(dynamic_cast<T2*>(&t), null_deleter<T2>());
}
template<typename T>
class stack_compatible_enable_shared_from_this
: public std::enable_shared_from_this<T>
{
public:
std::shared_ptr<T> shared_from_this()
{
try
{
return std::enable_shared_from_this<T>::shared_from_this();
}
catch (std::bad_weak_ptr&)
{
_local_ptr = stackobject_to_shared_ptr(*static_cast<T*>(this));
return _local_ptr;
}
}
std::shared_ptr<const T> shared_from_this() const
{
try
{
return std::enable_shared_from_this<T>::shared_from_this();
}
catch (std::bad_weak_ptr&)
{
_local_ptr = stackobject_to_shared_ptr(*const_cast<T*>(static_cast<const T*>(this)));
return _local_ptr;
}
}
private:
mutable std::shared_ptr<T> _local_ptr;
};
struct MyObj : public stack_compatible_enable_shared_from_this<MyObj>{};
int main (int argc, char **argv) {
//std::shared_ptr<MyObj> so = std::make_shared<MyObj>(6);
MyObj o{};
auto * so = &o;
{
auto l = std::weak_ptr<MyObj>(so->shared_from_this());
auto shared = l.lock();
if (shared) { } //use it
}
}