Mach7 icon indicating copy to clipboard operation
Mach7 copied to clipboard

Mach7 with variant: cannot access the matched sub-object

Open akrzemi1 opened this issue 8 years ago • 4 comments

I am using the library to switch on boost::variant and inspect/modify a given sub-type, as per the example below.

This doesn't work, because (to my knowedge) there is no way to get access to a mutable A from an instance of var<A&>.

Would it be possible to extend the interface of mch::var to get mutating access to its contained value?

#include <boost/variant.hpp>
#include <Mach7/code/type_switchN-patterns-xtl.hpp>
#include <Mach7/code/adapters/boost/adapt_boost_variant.hpp>
#include <Mach7/code/patterns/constructor.hpp>
#include <Mach7/code/patterns/primitive.hpp>

struct A
{
    int ma;
};
using V = boost::variant<A>;

void mutate_member(V& v)
{
    mch::var<A&> a;

    Match (v)
    {
        Case(mch::C<A>(a))
             a.ma = 2; // ERROR: cannot mutate a subobject of A
    }
    EndMatch
}

int main() {}

akrzemi1 avatar Feb 11 '16 16:02 akrzemi1

Here is how your broader mutating example from https://github.com/akrzemi1/sandbox/blob/master/mach7_with_variant.md can be written in Mach7 in the way typically done in other languages:

struct TankA { int va = 0; };
struct TankB { int vb = 0; };
struct TankX { };
using  Tank = boost::variant<TankA, TankB, TankX>;

namespace mch ///< Mach7 library namespace
{
template <> struct bindings<TankA> { Members(TankA::va); };
template <> struct bindings<TankB> { Members(TankB::vb); };
} // of namespace mch

int read(Tank const& tank)
{
  using namespace mch;

  var<const int&> v; // You create a reference to bind to a subcomponent

  Match(tank)
  {
    Case(C<TankA>(v)) return v;      // Once bound, you simply use it in the RHS
    Case(C<TankB>(v)) return v + 10;
    Case(C<TankX>())  return -1;
  }
  EndMatch
}

int write(Tank& tank)
{
  using namespace mch;

  var<int&> v;

  Match(tank)
  {
    Case(C<TankA>(v)) v = 1;  return v; // NOTE: You can also modify value bound via reference to a subcomponent
    Case(C<TankB>(v)) v = 42; return 0;
    Case(C<TankX>())  return -1;
  }
  EndMatch
}

int main()
{
    Tank t = TankB();
    assert (read(t) == 10);
    write(t);
    TankB* ptb = boost::get<TankB>(&t); 
    assert(ptb && ptb->vb == 42);
}

solodon4 avatar Feb 26 '16 07:02 solodon4

Thank you. That is quite interesting, and I start to see the expressiveness of the library. But let me change the problem a bit. Suppose all members of my types are private, and can only be accessed via public member functions. See "Challenge 2" in the same file: https://github.com/akrzemi1/sandbox/blob/master/mach7_with_variant.md

akrzemi1 avatar Feb 26 '16 15:02 akrzemi1

Whoa! I have just realized you can teach it to recognize member functions (getters). Impressive!

akrzemi1 avatar Feb 26 '16 18:02 akrzemi1

Yes, Members() macro accepts data members, nullary member functions and unary free-standing functions with the argument taking class as an argument. An example of the latter can be seen here:

https://github.com/solodon4/Mach7/blob/master/code/test/unit/example05.cpp#L67-L69

solodon4 avatar Feb 27 '16 22:02 solodon4