Saturday, November 27, 2010

Fun with switch statements

Since my coworker proudly told me about some switch macro he created for himself with C that can process strings, I thought I wanted to do that too, but with C++. Here is what I ended up with.
#include <boost/typeof/typeof.hpp>
#include <cassert>

/* Store a value of type V */
template<typename V>
struct S {
  operator int() { return -1; }
  S(V const& data):data(data), matched(false) { }

  V data;
  bool matched;
};

#define sswitch(V)                                      \
  switch(S<BOOST_TYPEOF(V)> _s_ = V)                    \
  case -1: { if(0)                                           

#define scase(V)                                        \
  } if(!(_s_.matched || _s_.data == V)) ; else          \
    if(!(_s_.matched = true)) ; else { case __LINE__

#define sdefault() \
  } default

#define snodefault(MSG) \
  } do { assert(!MSG); __builtin_unreachable(); } while(0)
That looks pretty weird! What are its drawbacks?
  • A default case must always be present and must be put last
  • Can't put a block around several cases, so sswitch is incompatible to Duffs device.
  • The to-be-switched value is always copied - there is no automatic deduction for lvalues such that they would use references and that only rvalues would be copied
On the goodness sides, sswitch can use native break keywords with the expected semantics, works with any type (not just strings) and also supports fall-through like the built-in switch. Here is how it can be used.
  sswitch(s) {
    scase("foo"): {
      std::cout << "s is foo" << std::endl;
      break; // could fall-through if we wanted
    }

    // supports brace-less style too
    scase("bar"):
      std::cout << "s is bar" << std::endl;
      break;

    // default must be at the end
    sdefault():
      std::cout << "neither of those!" << std::endl;
      break;
  }
Notice that since we insert extra braces between each case label (to prevent the code in between to be executed if we haven't hit a label yet), we can't use a plain "default:" at the end, as would be desired by me. We can jump inside if we want
  sswitch(s) {
  test:
    std::cout << "this will be output after foo/bar!" << std::endl;
    break;

    scase("foo"): {
      std::cout << "s is foo" << std::endl;
      goto test;
    }

    // supports brace-less style too
    scase("bar"):
      std::cout << "s is bar" << std::endl;
      goto test;

    // default must be at the end
    snodefault("neither foo nor bar!?");
  }

Please tell me what you think about it and whether you find any problem! Hope you like this little helper!

Saturday, July 3, 2010

Access to private members. That's easy!

So, always thought that it's impossible without undefined behavior to access private members of arbitrary classes without being friend.

Today I noticed I've been horribly wrong, after reading some insightful commit to the clang compiler, that enabled it to allow explicit instantiation to disregard accessibilities, as per the Standard. This enables us to access private members of others. As an experiment, I created some class templates

template<typename Tag>
struct result {
  /* export it ... */
  typedef typename Tag::type type;
  static type ptr;
};

template<typename Tag>
typename result<Tag>::type result<Tag>::ptr;

template<typename Tag, typename Tag::type p>
struct rob : result<Tag> {
  /* fill it ... */
  struct filler {
    filler() { result<Tag>::ptr = p; }
  };
  static filler filler_obj;
};

template<typename Tag, typename Tag::type p>
typename rob<Tag, p>::filler rob<Tag, p>::filler_obj;

So, how is it used? Let's have an example

struct A {
private:
  void f() {
    std::cout << "proof!" << std::endl;
  }
};

struct Af { typedef void(A::*type)(); };
template class rob<Af, &A::f>;

Ah, that's all to expose poor A's "f" member. Now anyone can use them using the member pointer snytax, as does the main function below

int main() {
  A a;
  (a.*result<Af>::ptr)();
}

Of course, as Herb Sutter told us, don't do these things in real code.