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