Sunday 27 April 2008

pocket C++ lambda library, part IIa


Are you wondering about the title of this entry? Well, it really should by part of a previous one*, but after looking at it I decided that the previous entry is pretty long already, and I wasn't willing to blow it up even more. And the theme isn't such an interesting one as to deserve a separate part in this mini-series. What is it we are talking about?

1. Access to the members


In the past I sometimes really wanted to be able to do the following:

    struct XXX { int value; int getValue() { return value; } };
    vecx<XXX*> vec_x;
    find_if(vec_x.begin(), vec_x.end(),  _$1->value == alive);
i.e. to access the members of an object out of the lambda function. Of course I'd like a code like _$1.value the best, but C++ doesn't allow us to overload the dot operator! Why not? This would mean that we could customize the method invocation mechanism itself! As it's the case in Groovy, Perl, Python or even Java**:

// Groovy:
Object invokeMethod(String name, Object args)
{
    log("Just calling me: $name");
    def result = metaClass.invokeMethod(this, name, args);
}
If you have that, you can do things like Rails in Ruby and builders in Groovy. You just intercept calls to the nonexisting methods (i.e. overload the methodMissing()/method_missing() in Groovy/Ruby or __getattr__() in Python) and install the "code block" (a closure, as to be exact) passed as one of the parameters in a custom hash map with the name parameter as key... You've got the message.

This isn't possible in C++, as it would introduce the metaclass notion into the language. At least it would require a common superclass for all C++ objects, and this contradicts the design of C++ classes (AFAIK) as thin wrappers for physical memory segments. On the other side, C++ has a more primitive notion of call intercepting: overloading of the -> operator! Alas, it only works with pointers, so we cannot provide a general solution for value based containers. An that is a bad thing enough.

So let's concentrate on the less ambitious goal: _$1->getValue()! Unfortunately, even this syntax canot be made work in our context! Why? Because for the C++ compiler the expression getValue() doesn't make sense! It doesn't refer to a class' method getValue(), which we could feed to the -> operator, it's just a string which we'd like to transform somehow in a function reference!

So what can we do (if anything)?

2. The Implementation


The best I could produce is the following:
    iterx = find_if(vecx.begin(), vecx.end(), _$1->*(&XXX::value) == 2);
    iterx = find_if(vecx.begin(), vecx.end(), _$1->*(&XXX::getValue) == 1);
Well, what can I say, it's passable. Now the compiler has a meaningful information in form of a member address, and it's not too ugly. How to implement it? With a standard technique from the first part***:
    // ->*
    template<class V, class O> struct ArrowStar : public lambda_expr {
        V O::* mptr;
        ArrowStar(V O::*m): mptr(m) { }
        V operator()(O* o) const { return o->*mptr; }
    };
we are overloading the member call operator for memebr access. For function member calls we need 2 more overloads. First for calls with 1 argument:
    template<class R, class O, class A> struct ArrowStarF : public lambda_expr {
        R(O::*fptr)(A);
        ArrowStarF(R(O::*f)(A)): fptr(f) { }
        R operator()(O* o, A a) const { return o->*fptr(a); }
    };
and for calls without arguments:
    template<class R, class O> struct ArrowStarFv : public lambda_expr {
        R(O::*fptr)();
        ArrowStarFv(R(O::*f)()): fptr(f) { }
        R operator()(O* o) const { return (o->*fptr)(); }
    }
nothing new here as well, just the standard operator overloading technique. For the == operation to work, I extended the EqTo operator from the part 1*** to do a little forwarding. I know, I should use the forwarders (like Le2_forw in part 1), but I was lazy:
    // lambda_expr ==
    template<class S, class T> struct EqTo : public lambda_expr {
        ...
        EqTo(S s, T t) : lexpr(s), val(t) { }
        template <class R>
            bool operator()(R r) { return lexpr(r) == val; }
    };
So let's do something useful at last:
    // shouldn't clash with lambda_expr: *_$1 <= *$2 !!!
    iterx = find_if(vecx.begin(), vecx.end(), _$1->*(&XXX::getVal) <= 2);
    // read field values from vecx
    vector<int> v10_1(10);
    transform(vecx.begin(), vecx.end(), v10_1.begin(), _$1->*(&XXX::getVal));
    // assign to a external counter
    int aaa;
    for_each(vecx.begin(), vecx.end(), aaa += _$1->*(&XXX::value));
Ok, the last one won't be working just now ;-), you must wait for the part 3 of the series! For the first one to work, we must extend the forwarding class from part 1*** a little for the case where only one side must be forwarded:
    template <class S, class T> struct Le2_forw : public lambda_expr
    {
        S e1;
        T e2;
        Le2_forw(S s, T t) : e1(s), e2(t) { }
        .....
        template <class U> // one side is bound! OPEN: assume left side!
            bool operator()(U a) const { return e1(a) <= e2; }
    };
And now everything is buzzing!

3. Discussion


If you think the sytax of the meber function call ist just horrible, there is another possibility to use the member functions: bind them! This you have seen (and frowned upon) in part 2*:
    for_each(vecx.begin(), vecx.end(), cout << bind(&XXX::getVal, _$1));
it's even less readable, is it? Or maybe not? Look, compared with _$1->*(&XXX::value) it's no more SUCH a bad sight! Maybe something like call_func(_$1, &XXX::getVal) would be more readable here? The advantage of this solution is that we could accept value objects in the container, and take it's address internally. I leave the decision to you, the implementation is more or less trivial.

Summing up, I couldn't achieve much progress here, because of inherent C++ design features. So maybe a macro solution? Or another level of indirection? What we really needed here is a hook for compiler errors (method not found), where we could install our own code snippet. Wait a minute! Something like compile time asserts? But how can I get around the string => function coding problem?

Something primitive like this would be possible:
    ...
    ArrowStarStr(string& name): fname(name) { }
    R operator()(O* o) const {
        return (o->func_hashtable.get(name))(); // and don't crash here!
    }
But you cannot use a POCppO anymore, you need some macro gadgetry like in Qt,
for example:
    struct XXX {
        int value;
        int getValue() { return value; }

        // now decorate:
        STORE_LAMDBA_FUNC(getValue);
    };
    // or property based:
    struct XXX {
        DEF_LAMBDA_PROPERTY(value, int);
    };
Not so pretty, not elegant, much to much effort needed. But it is the solution we have to use following the C++ language design. We just don't have introspection and cannot overload the dot. Sorry. Any ideas?

--
* http://ib-krajewski.blogspot.com/2008/01/c-pocket-lambda-library-part-2.html
** with dynamic proxies: see http://gleichmann.wordpress.com/2007/11/22/mimicry-in-action-dynamically-implement-an-interface-using-dynamic-proxy/
***http://ib-krajewski.blogspot.com/2007/12/c-pocket-lambda-library.html

Sunday 6 April 2008

Language trends and waiting blues.

Hi everbody! I recently skimmed over an interview* about programming language trends in the DDJ. In general, there was nothing new in there, rather a re-statement of the widely known programming trends. But some phrases caught my eye nonetheless:

PJ: C and C++ are definitely losing ground. There is a simple explanation for this. Languages without automated garbage collection are getting out of fashion.
....
Another language that has had its day is Perl. It was once the standard language for every system administrator and build manager, but now everyone has been waiting on a new major release for more than seven years. That is considered far too long.
As I'm mainly a C++ programmer in my bussines life, the news of the proceeding C++'s demise worry me, if only it acknowledges what I see with my own eyes. So this fact alone is not what I want to talk about, but rather about C++ similarity to Perl. Why?

Look, can't you see a parallel to the Perl's fate here? I cite: "everyone has been waiting on a new major release for more than seven years". And there's still no Perl 6! Recall, there were plans for Parrot**, a common VM for Perl and Python (or was it just a joke?). Everyone was excited, but nope, Python won't use Parrot, Parrot is only in its 0.x versions**, so Perl 6 won't come soon, and the situation generally is a mess.

Isn't that somehow similiar to the situation of C++? The last standard (or rather a correction of it) dates back to 1998, i.e. 10 years ago! It's even longer than Perl. So maybe C++'s retreat is due to lack of new language standard like in Perl's case? When I look at Java, I must admit I envy it. Just recall the evolution: while Java 4 was still rather a primitive language without much interesting features (sorry, perhaps with exception of proxies and introspection), already Java 5 brought foreach, generics, annotations, lock-free synchronisation, lock-free data structures, autoboxing and a new the memory model. Admittedly Java 6 wasn't that iteresting language-wise but Java 7 will get things like closures or fork-join support for easy multicore parallelism***! This gets you a wholly new, interesting language.

Contrast that with years-long discussion about what is to be included in the C++0x standard. And I still don't know what exactly is to come! Will a new memory model be included? And lambda functions? Garbage collection? Sometimes we all think that the new Standard won't be named C++0x, because years and years of discussion will be still needed!

So maybe the quote of Bjarne Stroustrup****:
"Java shows that a (partial) break from the past—supported by massive corporate backing — can produce something new. C++ shows that a deliberately evolutionary approach can produce something new — even without significant corporate support."
is false? Mabe only a corporate-backed language has a chance today? Look how quickly Java developed and how the new C++ standard stalls. But maybe it's the "design by committe"-effect on the side of C++? I don't know.

--
* Programming Languages: Everyone Has a Favorite One: ttp://www.ddj.com/cpp/207401593
** Parrot Virtual Machine: http://www.parrotcode.org/
*** Java theory and practice: Stick a fork in it, Part 1: ttp://www.ibm.com/developerworks/java/library/j-jtp11137.html
**** his interview of 2006: http://technologyreview.com/Infotech/17868/page3/