Modifying immutable substructures - c++

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";
}

Related

Common interface with a wrapper around std::variant or unions

This question is related to Enforcing a common interface with std::variant without inheritance.
The difference between that question and this one, is that I wouldn't mind inheritance, I am simply looking for the following structs/classes...
struct Parent { virtual int get() = 0; };
struct A : public Parent { int get() { return 1; } };
struct B : public Parent { int get() { return 2; } };
struct C : public Parent { int get() { return 3; } };
... to be AUTOMATICALLY "assembled" into a template:
template<typename PARENT, typename... TYPES>
struct Multi
{
// magic happens here
}
// The type would accept assignment just like an std::variant would...
Multi<Parent, A, B, C> multiA = A();
Multi<Parent, A, B, C> multiB = B();
Multi<Parent, A, B, C> multiC = C();
// And it would also be able to handle virtual dispatch as if it were a Parent*
Multi<Parent, A, B, C> multiB = B();
multiB.get(); // returns 2
Is this possible? If so, how? I would like to avoid working with handling pointers, as the use of std::variant/unions is intended to make memory contiguous.
You can't automagically set this up to allow multiB.get(), but you can allow multiB->get() or (*multiB).get() and even implicit conversion, by providing operator overloads:
template<typename Base, typename... Types>
struct Multi : std::variant<Types...>
{
using std::variant<Types...>::variant;
operator Base&() { return getref<Base>(*this); }
Base& operator*() { return static_cast<Base&>(*this); }
Base* operator->() { return &static_cast<Base&>(*this); }
operator const Base&() const { return getref<const Base>(*this); }
const Base& operator*() const { return static_cast<const Base&>(*this); }
const Base* operator->() const { return &static_cast<const Base&>(*this); }
private:
template<typename T, typename M>
static T& getref(M& m) {
return std::visit([](auto&& x) -> T& { return x; }, m);
}
};
You've probably encountered this kind of thing before when using iterators from the standard library.
Example:
int main()
{
Multi<Parent, A, B, C> multiA = A();
Multi<Parent, A, B, C> multiB = B();
Multi<Parent, A, B, C> multiC = C();
// Dereference
std::cout << (*multiA).get();
std::cout << (*multiB).get();
std::cout << (*multiC).get();
// Indirection
std::cout << multiA->get();
std::cout << multiB->get();
std::cout << multiC->get();
// Implicit conversion
auto fn = [](Parent& p) { std::cout << p.get(); };
fn(multiA);
fn(multiB);
fn(multiC);
}
Output:
123123123

Architecture pattern: function to handle 2 identical implementations

I have the following problem which I guess I am solving incorrectly given the problem I am facing:
I have an interface I and implementations A, B, C... I want to somehow express that I can get some results from couples (f(A, A), f(B, B), f(C, C)) and so on. In other words, I want to interface I to express that 2 identical implementations can be combined to produce some results, while others can be not (you can't get any valid result from f(A, B)).
Right now I have the following:
#include <iostream>
using namespace std;
class A;
class B;
class I{
public:
virtual int f (const I &other) const = 0;
virtual int fSpecific (const A &other) const { throw runtime_error(""); };
virtual int fSpecific (const B &other) const { throw runtime_error(""); };
};
class A: public I{
public:
A(int a) : a(a) {}
int f (const I &other) const override { other.fSpecific(*this); }
int fSpecific (const A &other) const override { /*logic here*/ return a + other.a; }
int a;
};
class B: public I{
public:
B(int b1, int b2) : b1(b1), b2(b2) {}
int f (const I &other) const override { other.fSpecific(*this); }
int fSpecific (const B &other) const override { /*logic here*/ return b1*b1 + b2*b2 + other.b1*other.b1 + other.b2*other.b2; }
private:
int b1;
int b2;
};
int f(const I &a, const I &b) {
a.f(b);
}
int main()
{
cout << f(A(1), A(2)) << std::endl; // prints 3
cout << f(B(1, 2), B(3, 4)) << std::endl; // prints 30
cout << f(A(1), B(3, 4)) << std::endl; // throws an error
return 0;
}
/*and so on*/
But I guess I use a wrong architecture. as adding classes results in changing I. Are there any better solution to express this such a relation?
Your interface is indeed strange, asking for method which should not be implemented.
We don't have multiple dynamic dispatch, except with std::visit of std::variant.
So following might help:
using V = std::variant<A, B, C>;
int f(const V& v1, const V& v2) {
struct {
template <typename T1, typename T2>
int operator()(const T& t1, const T2& t2) const { throw runtime_error(""); };
int operator()(const A& a1, const A& a2) const { return a1.a + a2.a; };
int operator()(const B& b1, const B& b2) const { return b1.b1*b1.b1 + b1.b2*b1.b2 + b2.b1*b2.b1 + b2.b2*b2.b2; };
int operator()(const C& c1, const C& c2) const { return c1.c * c2.c; };
} visitor;
return std::visit(visitor, v1, v2);
}
or keeping you hierarchy:
using CV = std::variant<const A*, const B*, const C*>;
class I
{
public:
virtual ~I() = default;
virtual CV asVariant() const = 0;
};
class A: public I{
public:
A(int a) : a(a) {}
CV asVariant() const override { return this; }
friend int f (const A& a1, const A& a2) { /*logic here*/ return a1.a + a2.a; }
int a;
};
class B: public I{
public:
B(int b1, int b2) : b1(b1), b2(b2) {}
CV asVariant() const override { return this; }
friend int f (const B& b1, const B& b2) {
/*logic here*/ return b1.b1*b1.b1 + b1.b2*b1.b2 + b2.b1*b2.b1 + b2.b2*b2.b2;
}
private:
int b1;
int b2;
};
int f(const I& i1, const I& &2) {
struct {
template <typename T1, typename T2>
int operator()(const T1*, const T2*) const { throw runtime_error(""); };
template <typename T>
int operator()(const T* t1, const T* t2) const { return f(*t1, *t2); };
} visitor;
return std::visit(visitor, i1.AsVariant(), i2.AsVariant());
}
You may employ dynamic_cast:
class I {
public:
template<typename T>
void fSpecific (T &other) {
if (dynamic_cast<T*>(this))
std::cout << "OK" << std::endl;
else
std::cout << "ERROR" << std::endl;
}
virtual ~I() {}
};
class A : public I {
};
class B : public I {
};
int main()
{
A a;
a.fSpecific(a);
B b;
b.fSpecific(a);
b.fSpecific((I&)a);
return 0;
}
There are some problems however:
Multiple inheritance
Objects need to be dynamically castable (that is why I added a virtual interface)
Casting to I also works.

Function passed as a class argument

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(); };

Creating an STL container of `container< container<Base> >`

Is there a way to do
container< container<Base> >
When you have a bunch of container<Derived>s that you wish to keep together (to iterator over?)
A concrete example follows.
Say you have
struct Animal { } ;
struct Dog : public Animal { } ;
struct StripedDog : public Dog { } ;
struct Cat : public Animal { } ;
struct SpottedCat : public Cat { } ;
You wish to keep collections of Cats, SpottedCats, Dogs & StripedDogs, in vectors or lists naturally,
vector<Dog*> doggies ;
vector<StripedDog*> stripedDoggies ;
vector<Cat*> catties ;
vector<SpottedCat*> spottedCatties ;
You wish to iterate over ALL animals, however, so you wish to slam references to all dogs & cats collections into a single object,
vector< vector<Animal *>* > zoo ;
zoo.push_back( &doggies ) ;
zoo.push_back( &stripedDoggies ) ;
zoo.push_back( &catties ) ;
zoo.push_back( &spottedCatties ) ;
So now you can
feed( zoo ) ;
Of course, this doesn't compile. The vectors of cats & dogs are not vector<Animal*>, but rather vectors of their concrete types. Without retaining redundant lists and without losing the concrete type info (ie NOT using lists of base type Animal* as in vector<Animal*> stripedDoggies), is there a way to achieve equivalent behavior from C++?
I think you're looking at something like this, but not quite sure. Let me know if it isn't near to what you need and I'll drop it for favor of someone that does. The output demonstrates the virtual feed() operation does its business appropriately. Arranging a potential variadic parameter pack to that function would take me some time to cook on for awhile, and I'm not even sure it is possible.
But this should get you close.
#include <iostream>
#include <algorithm>
#include <type_traits>
#include <vector>
// base. enforces inheritance by SFINAE
template<typename Base, typename T, template<typename, typename...> class V>
typename std::enable_if<std::is_base_of<Base, T>::value>::type
invoke(void (Base::*func)(), const class V<T*>& vec)
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
for (auto p : vec)
(p->*func)();
}
// chain.
template<typename Base, typename T, template<typename, typename...> class V, typename... Args>
typename std::enable_if<std::is_base_of<Base, T>::value>::type
invoke(void (Base::*func)(), const class V<T*>& vec, Args... args)
{
invoke(func, vec);
invoke(func, args...);
}
int main()
{
struct Animal
{
virtual void feed()
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
} ;
struct Dog : public Animal
{
void feed()
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
} ;
struct StripedDog : public Dog {};
struct Cat : public Animal {};
struct SpottedCat : public Cat {};
std::vector<Dog*> doggies ;
std::vector<StripedDog*> stripedDoggies ;
std::vector<Cat*> catties ;
std::vector<SpottedCat*> spottedCatties ;
Dog dog;
doggies.push_back(&dog);
StripedDog sdog;
stripedDoggies.push_back(&sdog);
Cat cat;
catties.push_back(&cat);
invoke(&Animal::feed, doggies, stripedDoggies, catties, spottedCatties);
return 0;
}
Output
typename std::enable_if<std::is_base_of<Base, T>::value>::type invoke(void (Base::*)(), const class V<T *> &) [Base = Animal, T = Dog, V = vector]
virtual void main()::Dog::feed()
typename std::enable_if<std::is_base_of<Base, T>::value>::type invoke(void (Base::*)(), const class V<T *> &) [Base = Animal, T = StripedDog, V = vector]
virtual void main()::Dog::feed()
typename std::enable_if<std::is_base_of<Base, T>::value>::type invoke(void (Base::*)(), const class V<T *> &) [Base = Animal, T = Cat, V = vector]
virtual void main()::Animal::feed()
typename std::enable_if<std::is_base_of<Base, T>::value>::type invoke(void (Base::*)(), const class V<T *> &) [Base = Animal, T = SpottedCat, V = vector]
Sorry about having to scroll to the right to see the types in that pretty-print, but they're pretty telling and should be looked at to see how this works. Note that both Dog and StripedDog containers properly fire the Dog::feed() member, while the Cat container properly fires the Animal::feed() base member since it provides no override.
Good luck, and I hope it helps.
Since you are using pointers, which are quite cheap to copy, you could probably do:
vector< Animal * > zoo;
zoo.append( zoo.end(), doggies.begin(), doggies.end() );
// ditto with the others
feed( zoo ); // just receives *one* vector with animals to feed
Another option if you don't want to copy/merge the vectors:
void feed() {}
template< typename V >
void feed( const V& v )
{
for( A* a : v )
{
// ...do something with 'a'
}
}
template< typename V, typename V2, typename... Vs >
void feed( const V& v, const V2& v2, const Vs&... vs )
{
feed( v );
feed( v2, vs... );
}
Now you can call feed( doggies, stripedDoggies, catties, spottedCatties );.
In general, if you are doing something like this, you would store them all in a single vector:
std::vector<std::shared_ptr<Animal>> animals;
If Animal defines a feed method, iterating over it simply means calling that function:
animals[i]->feed();
If you want to call specific functions based on types, you'll need to do some casting:
std::shared_ptr<Dog> pDog = std::dynamic_pointer_cast<Dog>(animals[i]);
std::shared_ptr<Cat> pCat = std::dynamic_pointer_cast<Cat>(animals[i]);
// other casts
if (pDog)
{
// do something with a dog
}
else if (pCat)
{
// do something with a cat
}
// etc
If you really wanted to store all of the animals in additional vectors, you can do that by wrapping the whole zoo:
class Zoo
{
private:
std::vector<std::shared_ptr<Animal>> animals;
std::vector<std::shared_ptr<Dog>> dogs;
// other vectors
public:
void AddDog(const Dog& d)
{
std::shared_ptr<Dog> pD = std::make_shared<Dog>(d);
dogs.push_back(pD);
std::shared_ptr<Animal> pA = std::static_pointer_cast<Animal>(pD);
animals.push_back(pA);
}
};
It doubles the number of pointers you are storing in memory, but pointers are fairly cheap. Then you can pass the whole zoo, or individual animal types without the need to do the casting every time.
Polymorphic iterators? shudder
Usage example:
#include <iostream>
struct Animal
{
virtual void print() = 0;
};
struct Elephant : Animal
{
virtual void print() override { std::cout << "toot"; }
};
struct Cat : Animal
{
virtual void print() override { std::cout << "meow"; }
};
struct Fox : Animal
{
virtual void print() override
{ std::cout << "ring-ding-ding-ding-dingeringeding"; }
};
#include <vector>
template<class T>
using simple_vector = std::vector<T>;
int main()
{
std::vector<Elephant> elephants(5);
std::vector<Cat> cats(3);
std::vector<Fox> foxes(1);
polymorphic_range_container<simple_vector, Animal> animals;
animals.push_back( std::make_pair(elephants.begin(), elephants.end()) );
animals.push_back( std::make_pair(cats.begin(), cats.end()) );
animals.push_back( std::make_pair(foxes.begin(), foxes.end()) );
for(auto& animal : animals)
{
animal.print(); std::cout << ", ";
}
std::cout << std::endl;
}
Implementation (basics):
#include <memory>
#include <algorithm>
#include <iterator>
#include <utility>
template<class T>
struct iterator_base
{
virtual void advance(int i) = 0;
virtual T& get() const = 0;
virtual bool equal(iterator_base const&) const = 0;
};
template<class ValueType, class Container, class Id>
struct polymorphic_iterator
{
polymorphic_iterator& operator++()
{
impl->advance(1);
if(container->is_end(*impl, id))
{
impl = container->next(id);
}
return *this;
}
ValueType& operator*() const { return impl->get(); }
friend bool operator==(polymorphic_iterator const& l,
polymorphic_iterator const& r)
{
if(l.impl == nullptr) return r.impl == nullptr;
if(r.impl == nullptr) return false;
return l.impl->equal( *(r.impl) );
}
friend bool operator!=(polymorphic_iterator const& l,
polymorphic_iterator const& r)
{
return not (l == r);
}
private:
std::unique_ptr< iterator_base<ValueType> > impl;
Container* container;
Id id;
friend Container;
polymorphic_iterator(Container* pc, Id pid, decltype(impl) p)
: impl( std::move(p) ), container(pc), id(std::move(pid))
{}
};
template<template<class> class Container, class Base>
class polymorphic_range_container
{
private:
using self = polymorphic_range_container;
struct IRange
{
using piterator = std::unique_ptr < iterator_base<Base> >;
virtual piterator begin() = 0;
virtual piterator end() = 0;
};
template<class It>
struct range : IRange
{
range(It p_begin, It p_end) : m_begin(p_begin), m_end(p_end) {}
using typename IRange::piterator;
piterator begin() override { return piterator{new iterator_impl(m_begin)}; }
piterator end() override { return piterator{new iterator_impl(m_end)}; }
private:
struct iterator_impl : iterator_base<Base>
{
iterator_impl(It p) : it(p) {}
virtual void advance(int i) override { std::advance(it, i); }
virtual Base& get() const override { return *it; }
virtual bool equal(iterator_base<Base> const& other) const override
{
iterator_impl const* pOther
= dynamic_cast<iterator_impl const*>(&other);
if(nullptr == pOther) return false;
else return it == pOther->it;
}
private:
It it;
};
iterator_impl m_begin;
iterator_impl m_end;
};
using container_type = Container< std::unique_ptr<IRange> >;
container_type ranges;
public:
template<class T>
void push_back(std::pair<T, T> p_range)
{
ranges.push_back( std::unique_ptr<IRange>{new range<T>(p_range.first, p_range.second)} );
}
using iterator = polymorphic_iterator<Base, self, typename container_type::const_iterator>;
iterator begin()
{
return iterator{this, ranges.cbegin(), ranges.front()->begin()};
}
iterator end()
{
return iterator{this, ranges.cend(), {nullptr}};
}
private:
friend iterator;
std::unique_ptr< iterator_base<Base> > next(typename container_type::const_iterator& p)
{
++p;
if(p == ranges.end()) return {nullptr};
else return (**p).begin();
}
bool is_end(iterator_base<Base> const& it, typename container_type::const_iterator const& id)
{
if(ranges.end() == id) return false;
else return (**id).end()->equal(it);
}
};

Avoid switching on types to allow constant folding

I am trying to find a class hierarchy that permits to implement place holders for processor registers and operations on it. It should also allow for constants to be folded at run time. For sake of simplicity I'll only look at one operation, here multiplication. Place holders and constants should be accessible uniformly, i.e. have a common base class.
The code below defines the following classes:
class A: Base class for place holders (registers) and constants
class B: Place holder for a register (its structure holds the name of it)
class C: Base of all constants
class CI: int constant
class CF: float constant
#include <iostream>
#include <memory>
#include <cassert>
class A {
public:
virtual ~A(){}
};
class B : public A {
};
class C : public A {};
class CI : public C {
public:
typedef int Type_t;
int getValue() {return 1;}
};
class CF : public C {
public:
typedef float Type_t;
float getValue() {return 1.1;}
};
typedef std::shared_ptr<A> Aptr;
typedef std::shared_ptr<B> Bptr;
typedef std::shared_ptr<C> Cptr;
typedef std::shared_ptr<CI> CIptr;
typedef std::shared_ptr<CF> CFptr;
template<class T, class T2> struct promote {};
template<> struct promote<float,int> { typedef float Type_t; };
template<> struct promote<float,float> { typedef float Type_t; };
template<> struct promote<int,float> { typedef float Type_t; };
template<> struct promote<int,int > { typedef int Type_t; };
template<class T1, class T2>
typename promote<typename T1::element_type::Type_t,
typename T2::element_type::Type_t>::Type_t
mul_const( const T1& c1 , const T2& c2 )
{
std::cout << c1->getValue() * c2->getValue() << "\n";
return c1->getValue() * c2->getValue();
}
template<class T>
std::shared_ptr<T> get(const Aptr& pA) {
return std::dynamic_pointer_cast< T >( pA );
}
Aptr create_A(float f) { return std::make_shared<A>(); }
Aptr create_A(int i) { return std::make_shared<A>(); }
Aptr mul_const( const Cptr& cp1 , const Cptr& cp2 )
{
if (auto c1 = get<CI>(cp1))
if (auto c2 = get<CF>(cp2)) {
return create_A( mul_const(c1,c2) );
}
if (auto c1 = get<CF>(cp1))
if (auto c2 = get<CI>(cp2)) {
return create_A( mul_const(c1,c2) );
}
if (auto c1 = get<CI>(cp1))
if (auto c2 = get<CI>(cp2)) {
return create_A( mul_const(c1,c2) );
}
if (auto c1 = get<CF>(cp1))
if (auto c2 = get<CF>(cp2)) {
return create_A( mul_const(c1,c2) );
}
assert(!"oops");
}
Aptr mul( const Aptr& pA1, const Aptr& pA2 )
{
if (auto c1 = get<C>(pA1))
if (auto c2 = get<C>(pA2))
{
return mul_const(c1,c2);
}
}
int main()
{
Aptr pA1( new CF );
Aptr pA2( new CI );
Aptr result = mul( pA1, pA2 );
}
The problem I am having with the above code is the function Aptr mul_const( const Cptr& cp1 , const Cptr& cp2 ). It contains basically switching of types for all possible combinations of constant types. It works, but I would like to know if this can be done more elegantly?
I guess you could do what the compiler does, and convert the other parameter to float when one is float. You'll probably need a new function to do the conversion and a "isFloat" (or "isInt"). I'm not convinced it gives you that much benefit, really...
// Add two virtual member functions here:
class C : public A {
public:
virtual bool isInt() = 0;
virtual float getAsFloat() = 0;
};
Then implement:
class CI : public C {
public:
typedef int Type_t;
int getValue() {return 1;}
float getAsFloat() { return getValue(); }
bool isInt() { return true; }
};
class CF : public C {
public:
typedef float Type_t;
float getValue() {return 1.1;}
float getAsFloat() { return getValue(); }
bool isInt() { return false; }
};
Now, your mul_const becomes:
Aptr mul_const( const Cptr& cp1 , const Cptr& cp2 )
{
if (cp1.isInt() && cp2.isInt())
{
CIptr c1 = get<CI>(cp1));
CIptr c2 = get<CI>(cp2));
std::cout << c1->getValue() * c2->getValue() << "\n";
return CIptr(c1->getValue() * c2->getValue());
}
else
{
std::cout << cp1->getAsFloat() * cp2->getAsFloat() << "\n";
return CFptr(cp2->getAsFloat() * cp2->getAsFloat());
}
// This becomes unreachable... Probably OK to delete.
assert(!"oops");
}
[And I think a few template parts can be deleted... ]