Robert Bradshaw wrote:
I start with a disclaimer I should have made several posts back: I have done very little programming with C++, so please excuse any naiveté I have in this regard...

Sure, but I must also expose it :-)

So, to sum up the two solutions so far:

A) Introduce the C++ operators to Cython. This will require adding C++ semantics to the Cython language in other areas as well.

B) Define C++ snippets of code which correspond to Cython operators/semantics. This will only support a (natural) subset of what is possible to do in C++; say, as if the C++ library had been wrapped for the Python runtime.

We seem to agree that A should use C++ syntax (that's really a tangential issue but is a nice peg to remember things by).

I argue below that A must have effects far beyond simple operator overloading.

On Apr 25, 2009, at 9:54 AM, Dag Sverre Seljebotn wrote:
How would you represent my latter example in my previous mail at using C++
syntax in Cython?

I would expose your ItemAccessor class as well as your MyCollection class in the Cython declaration. This may necessitate exposing the notion of a C++ reference to Cython as well. At the very least, I think trying to overload on the return type is bad.

(Note that C++ overloading on return type in the specific case of the conversion operator, "operator SomeType". I.e. you can have "int a = x; float b = x" invoke two different methods on an x object to do the conversion.)

Exposing ItemAccessor as is, and support its use, will have a dramatic effect on the Cython language, as operator= is what is really used here.

I've attached some C++ code to demonstrate the full example. Note that it is possible to do:

if (1 == 1) {
  ItemAccessor acc = some_collection[2];
  acc = 3.2;
  cout << some_collection[2] << endl; // outputs 3.2
}

Now, translate this to Cython to see how bad this looks:

if 1 == 1:
    cdef ItemAccessor acc = some_collection[2] # C++ var scoping?

    acc = 3.2
    # assignment triggers operator= but leaves
    # existing object in place?

    print some_collection[2]
# upon exiting if-tests scope, the
# destructor of ItemAccessor is run which could do anything..


Summary:
 - C++ does *only* have "getitem", not "setitem".
- However, we return an object with overriden operator= and "operator double" in order to simulate setitem. - operator[] is not what is at stake here, one could also have a method return an ItemAccessor:

some_collection.get(2) = 3.2;

This is just one example -- that doesn't mean that it's a corner case, it means that there are a hundred other cases which are equally different to represent sanely with Cython semantics. C++ code is just pieced together in a different way, where many C++ objects and operators can make up one statement in Python.

This is what I think conflicts with letting the operator[] notation into
Cython. operator[] means nothing of the sort of what we are really
supporting.

Hmm... OK, maybe I'll take back that statement and say Cython does need to go deeper than this. Right now one has to learn the "magic" of giving Cython declarations to generate the right code, which is undesirable. I would rather not make users have to learn a new "magic" but really state what's going on on the C++ side.

The only two non-simple lvalues we need to worry about are indexing and attribute access, right?

You mean Cython-side? Yes. It's all we can support with the current Pythonic syntax.

C++ can do "anything", like

some_collection.get(2) = 3.2;

or whatever, just overload "operator=" on the lhs return value.

The real issue here is with C++ allowing objects as stack variables with copy constructors and operator= etc. etc.; things I personally don't want to have in the Cython language. And then some kind of "wrapping" where the semantics are not 1:1 is needed.

There are lots of other cases where Cython cannot easily express C++ code without modification: - ++ is not the same as += 1, e.g. linked list types would support only the first on iterators - a->b and (*a).b could have different values and types, just overload operator-> differently from operator*.

All of these can be relatively easily handled if we have a philosophy where we make it easy to wrap "sane" code but don't support the whole C++ set, which seem to imply (B).

--
Dag Sverre
#include <iostream>

using namespace std;

class MyColl;

class ItemAccessor {
private:
  MyColl& fColl;
  int fKey;
public:
  ItemAccessor(MyColl& coll, int key) : fColl(coll), fKey(key) { }
  double operator=(double value);
  operator double();
};

class MyColl {
private:
  double fStore[10];
  friend class ItemAccessor;
public:
  MyColl() { cout << "constructing MyColl" << endl; }
  ~MyColl() { cout << "destructing Mycoll" << endl; }  
  ItemAccessor operator[](int key) {
    return ItemAccessor(*this, key);
  }
  ItemAccessor get(int key) {
    return (*this)[key];
  }
};

double ItemAccessor::operator=(double value) {
  cout << "Setting [" << fKey << "] to " << value << endl;
  fColl.fStore[fKey] = value;
  return value;
}

ItemAccessor::operator double() {
  double result = fColl.fStore[fKey];
  cout << "Getting [" << fKey << "] which was " << result << endl;
  return result;
}

int main() {
  cout << "start" << endl;
  if (1 == 1) {
    MyColl col;
    ItemAccessor acc = col[2];
    acc = 3;
    col.get(3) = 3.14;
    cout << col[2] << ' ' << col[3] << endl;
  }
  cout << "end" << endl;
}

/*
OUTPUT:

start
constructing MyColl
Setting [2] to 3
Setting [3] to 3.14
Getting [3] which was 3.14
Getting [2] which was 3
3 3.14
destructing Mycoll
end


*/
_______________________________________________
Cython-dev mailing list
[email protected]
http://codespeak.net/mailman/listinfo/cython-dev

Reply via email to