From 5OPw56lwv at worktow1est.com Thu Jan 1 11:57:10 1998 From: 5OPw56lwv at worktow1est.com (5OPw56lwv at worktow1est.com) Date: 01 Jan 98 11:57:10 PM Subject: [C++-SIG] Give Your Child "One of the Best Children's Videos"" Message-ID: The holidays are upon us. If you're like a lot of people, you struggle to find gifts for your children that will entertain and amuse them at the same time. Well, here's a gift that will delight your child - A Is For Airplane! "A Is For Airplane" is the award-winning educational video that shows kids all the fun and teamwork involved in running an airline. "A Is For Airplane" gets viewers behind the scenes at the airport! Kids get to see: * The ticket counter! * Inside the baggage system! * On the ramp with the baggage loaders and fuelers! * In the catering kitchens! * Inside the control tower! * In the hangar with the mechanics! * At the boarding gate! * And even in the COCKPIT of a real Boeing 757! Parenting Magazine calls "A Is For Airplane" "One of the Best Videos of 1996!" It's also Approved by the Parent's Choice Foundation! Thousands of copies of "A Is For Airplane" have been sold for $14.95, but as an Internet Special this holiday season you can get "A Is For Airplane" for only $11.95 (plus shipping and handling.) ORDER TODAY FOR GUARANTEED HOLIDAY DELIVERY! You can order "A Is For Airplane" by calling our toll-free number - 800-250-4210. If you'd like more information, visit our Website at www.ppmm.com/jfp/jfp1297.htm or CLICK HERE! Thank you for your time... Johnson Family Productions Madison, WI _______________ C++-SIG - SIG for Development of a C++ Binding to Python send messages to: c++-sig at python.org administrivia to: c++-sig-request at python.org _______________ From dubois1 at llnl.gov Wed Jan 7 20:12:41 1998 From: dubois1 at llnl.gov (Paul F. Dubois) Date: Wed, 7 Jan 1998 11:12:41 -0800 Subject: [C++-SIG] Some progress: example.cpp Message-ID: <01bd1ba0$39b01400$998a7380@pduboispc> I have been developing some classes to encapsulate the Python API. This message will contain an example of use. A second message will contain the main header file. I'm not sending out all the pieces and I don't mean for anyone to use it yet, as it has not been tested properly. However, I thought it might be useful to have others see it and comment at this point. Unlike our previous effort, this work should compile with many compilers (I used Microsoft Visual C++ so far). While it uses the STL it does not itself have templated functions or classes. It has an emphasis on safety and cleanliness rather than speed or cleverness. It has a single inheritance hierarchy rooted at class Object (in namespace Py). Each Object holds a PyObject* which it owns a reference to. More specific Objects, such as Tuple, verify their Tupleness at the moment of creation or assignment. When something goes wrong, an exception derived from the class PyException is thrown, thus cleaning up all the Objects in the current scope (and thus fixing up all the reference counts of the pointers owned by local Objects). Here is a typical extension file. It has two methods "sum" (sums up the floating-point arguments) and "test" (which calls a lot of test routines). Note that any PyExceptions are caught and turned back into Python exceptions. (Py_Null is just a correctly cast 0) #include "Python.h" #include "CXX_Objects.h" using namespace Py; static PyObject * ex_sum(PyObject* self, PyObject* args) { Tuple t(args); Float f, g; int i; f = 0.0; try { for(i = 0; i < t.length(); i++) g = t[i]; f = f + g; } return new_reference_to(f); } catch(const PyException&) { return Py_Null; } } static String test_String() { String s("hello"); String t("world in brief", 6); s += " "; t *= 2; s += t; return s; } static Object test_numbers() { // test the basic numerical classes List result; Int i; Int j(2); Int k = Int(3); i = j + k + 1; Float a; a = 3 + i; Float b(3.0); result.append(i); result.append(1.0 + 2*a + (b*3.0)/2.0 + k/Float(i) + 2); Float c; c = k; result.append(c); return result; } static List test_List() { // test the List class List a; Object b(Py_None); Int i(3); Float x(6.0); Float c(10.0), d(20.0); a.append(i); a.append(x); a.append(Float(0.0)); b = a[0]; a[2] = b; a.append(c+d); a.append(a.getSlice(0,2)); return a; } static Dict test_Dict() { // test the Dict class Dict a; String s("ho"); a["hi"] = s; a["h"] = s[0]; Dict b; List v; b["key 1"] = s; b["key 2"] = Float(2.0); v = b.values(); a["bkeys"] = b.keys(); a["bvalues"] = v; b.clear(); if(b.keys().length() != 0) { throw PyException_RuntimeError("dict clear failed"); } return a; } static Tuple test_Tuple() { // test the Tuple class Tuple a(3); Tuple t1; Float f1(1.0), f2(2.0), f3(3.0); a[0] = f1; // should be ok since no other reference owned a[1] = f2; a[2] = f3; Tuple b(a); t1 = a; try { t1[0] = Int(1); // should fail } catch (const PyException& e) { e.clear(); cout << "OK, incorrect tuple assignment prevented\n"; } return Tuple(*(b.repeat(2))); } static PyObject* ex_test (PyObject* self, PyObject* args) { try { cout << "Numbers: " << test_numbers() << "\n"; cout << "String: " << test_String() << "\n"; cout << "List: " << test_List() << "\n"; cout << "Dict: " << test_Dict() << "\n"; Tuple t(args); List a(t); Tuple c(a); cout << "List from tuple: " << a << "\n"; cout << "Tuple from list: " << c << "\n"; pair result = coerce(Float(1.0), Int(2)); cout << "Result of coerce: " << result.first << ", " << result.second << "\n"; cout << "Result of +: " << Float(1.0) + Int(2) << "\n"; cout << "Result of -: " << Float(1.0) - Int(2) << "\n"; cout << "Result of *: " << Float(1.0) * Int(2) << "\n"; cout << "Result of /: " << Float(1.0) / Int(2) << "\n"; cout << "Result of 7 % 2: " << (Int(7) % Int(2)) << "\n"; cout << "Result of -7: " << -Int(7) << "\n"; cout << "Result of +7: " << +Int(7) << "\n"; cout << "Result of abs(-7): " << abs(Int(-7)) << "\n"; return Nothing; } catch (const PyException&) { return Py_Null; } } static PyMethodDef example_methods[] = { {"sum", ex_sum, 1, "sum(arglist)"}, {"test", ex_test, 1, "test(arglist)"}, {NULL, NULL} }; extern "C" { void initexample() { Py_InitModule("example", example_methods); } } _______________ C++-SIG - SIG for Development of a C++ Binding to Python send messages to: c++-sig at python.org administrivia to: c++-sig-request at python.org _______________ From dubois1 at llnl.gov Wed Jan 7 20:14:20 1998 From: dubois1 at llnl.gov (Paul F. Dubois) Date: Wed, 7 Jan 1998 11:14:20 -0800 Subject: [C++-SIG] some progress: CXX_Objects.h Message-ID: <01bd1ba0$751b90a0$998a7380@pduboispc> This is the second part of a message about my recent work. //----------------------------------*-C++-*--------------------------------- -// // Copyright 1996 The Regents of the University of California. // All rights reserved. //-------------------------------------------------------------------------- -// #ifndef __CXX_Objects__h #define __CXX_Objects__h #include "Python.h" #include "CXX_Exception.h" #include #include using namespace std; namespace Py { // Macro for assertion checking #define Require(something) \ {if(!(something)) throw PyException (#something);} // Forward declarations class Object; class Type; class Float; class Int; class Sequence; class String; class Tuple; class List; class Mapping; class Dict; // new_reference_to PyObject* new_reference_to (const Object& g); // return an owned reference to the object g owns. PyObject* new_reference_to (PyObject* p); // return an owned reference to *p // Py_Null is a PyObject* (0) for signalling error returns #define Py_Null (static_cast(0)) // Nothing is what a non-function returns #define Nothing new_reference_to(Py_None) class FromAPI { // Python API routines return to you owned pointers. // FromAPI is a helper class to let you construct an object from one of these. // Usage: Object(FromAPI(p)) or Object x = FromAPI(p) // where p is an already-owned pointer such as returned by an API routine. private: PyObject *p; FromAPI& operator=(const FromAPI& other); // no assignment FromAPI(const FromAPI& other); // no copy constructor public: explicit FromAPI(PyObject *powned) { // construct from a pointer you own, only! p = powned; } virtual ~FromAPI() { Py_XDECREF(p); // we own it, so kill it } operator PyObject*() {return p;} }; //========================================================================== =// // class Object // The purpose of this class is to serve as the most general kind of // Python object, for the purpose of writing C++ extensions in Python // Objects hold a PyObject* which they own. This pointer is always a // valid pointer to a Python object. In children we must maintain this behavior. // // Instructions on how to make your own class MyType descended from Object: // (0) Pick a base class, either Object or perhaps Sequence or Mapping. // This example assumes Object. // (1) Write a routine int MyType_Check (PyObject *) modeled after PyInt_Check, // PyFloat_Check, etc. // (2) Add method accepts: // virtual bool accepts (PyObject *pyob) const { // return pyob && MyType_Check (pyob); // } // (3) Include the following constructor and copy constructor // | // explicit MyType (PyObject *pyob): Object(pyob) { // validate(); // } // MyType(const MyType& other): Object(other) { // validate(); // } // You may wish to add other constructors; see the classes below for examples. // Each constructor must end by validating the pointer you have created. // (4) Each class needs at least these two assignment operators: // MyType& operator= (const Object& rhs) { // return (*this = *rhs); // } // Mytype& operator= (const PyObject* rhsp) { // if(*this == rhsp) return *this; // set(rhsp); // return *this; // } // Object equality means they contain the same PyObject* int operator==(const Object& o1, const Object& o2); int operator==(const Object& o1, const PyObject *p2); int operator==(const PyObject *p1, const Object& o2); int operator!=(const Object& o1, const Object& o2); int operator!=(const Object& o1, const PyObject *p2); int operator!=(const PyObject *p1, const Object& o2); class Object { protected: PyObject* p; // the pointer to the Python object void set (const PyObject* pyob) { release(); p = const_cast(pyob); Py_XINCREF (p); validate(); } void release () { Require((p==Py_Null) || (reference_count() > 0)); Py_XDECREF (p); p = Py_Null; } void validate() { // release pointer if not the right type if (! accepts (p)) { release (); if(PyErr_Occurred()) { // Error message already set throw PyException(); } string s("Error creating object of type "); s += (typeid (*this)).name(); throw PyException_TypeError (s.c_str()); } } // Constructs an illegal object; child has to fix this fast! Object (): p(Py_Null) {} public: // Note on accepts: constructors call the base class // version of a virtual when calling the base class constructor, // so the test has to be done // explicitly in a descendent. Not so for the assignment operator. // Constructors acquire new ownership of pointer explicit Object (PyObject* pyob): p (pyob) { Py_XINCREF (p); validate(); } // Copy constructor acquires new ownership of pointer Object (const Object& ob): p(*ob) { Py_XINCREF (p); validate(); } // Assignment acquires new ownership of pointer Object& operator= (const Object& rhs) { return (*this = *rhs); } Object& operator= (const PyObject* rhsp) { if(*this == rhsp) return *this; set (rhsp); return *this; } // Destructor virtual ~Object () { release (); } // Loaning the pointer to others, retain ownership PyObject* operator* () const return p; } PyObject* pointer () const { return p; } // // Queries // // Can pyob be used in this object's constructor? virtual bool accepts (PyObject *pyob) const { return (pyob != Py_Null); } int reference_count () const { // the reference count return p ? p->ob_refcnt : 0; } Type type () const; // the type object associated with this one String str () const; // the str() representation String repr () const; // the repr () representation int hasAttr (const char *s) const { return PyObject_HasAttrString (p, const_cast(s)); } Object getAttr (const char *s) const { return Object(FromAPI( PyObject_GetAttrString (p, const_cast (s)) )); } Object getItem (const Object& key) const { return Object(FromAPI(PyObject_GetItem(p, *key))); } long hashValue () const { return PyObject_Hash (p); } // int print (FILE* fp, int flags=Py_Print_RAW) { // return PyObject_Print (p, fp, flags); // } int isCallable () const { return PyCallable_Check (p); } int isMapping () const { return PyMapping_Check (p); } int isNumeric () const { return PyNumber_Check (p); } int isSequence () const { return PySequence_Check (p); } int isTrue () const { return PyObject_IsTrue (p); } int isType (const Type& t) const; int isTuple() const { return PyTuple_Check(p); } // Commands void setAttr (const char* attr_name, const Object& value) { if(PyObject_SetAttrString (p, const_cast(attr_name), *value) == -1) throw PyException_AttributeError ("getAttr failed."); } void delAttr (char *s) { if(PyObject_DelAttrString (p, s) == -1) throw PyException_AttributeError ("delAttr failed."); } void setItem (const Object& key, const Object& value) { if(PyObject_SetItem(p, *key, *value) == -1) throw PyException_KeyError("setItem failed."); } void delItem (const Object& key) { if(PyObject_DelItem(p, *key) == -1) throw PyException_KeyError("delItem failed."); } }; // End of class Object ostream& operator<< (ostream& os, const Object& ob); // Class Type class Type: public Object { public: explicit Type (PyObject* pyob): Object(pyob) { validate(); } explicit Type (const Object& ob): Object(*ob) { validate(); } Type(const Type& t): Object(t) { validate(); } Type& operator= (const Object& rhs) { return (*this = *rhs); } Type& operator= (const PyObject* rhsp) { if(*this == rhsp) return *this; set (rhsp); return *this; } virtual bool accepts (PyObject *pyob) const { return pyob && PyType_Check (pyob); } }; // =============================================== // class Int class Int: public Object { public: // Constructor explicit Int (PyObject *pyob): Object (pyob) { validate(); } Int (const Int& ob): Object(*ob) { validate(); } // create from long explicit Int (long v = 0L): Object(FromAPI(PyInt_FromLong(v))) { validate(); } // create from int explicit Int (int v) { long w = v; p = PyInt_FromLong(w); validate(); } explicit Int (const Object& ob): Object() { p = PyNumber_Int(*ob); validate(); } // Assignment acquires new ownership of pointer Int& operator= (const Object& rhs) { return (*this = *rhs); } Int& operator= (const PyObject* rhsp) { if(*this == rhsp) return *this; set (FromAPI(PyNumber_Int(const_cast(rhsp)))); return *this; } // Membership virtual bool accepts (PyObject *pyob) const { return pyob && PyInt_Check (pyob); } // convert to long operator long() const { return PyInt_AsLong (p); } // assign from an int Int& operator= (int v) { *this = FromAPI(PyInt_FromLong (long(v))); return *this; } // assign from long Int& operator= (long v) { *this = FromAPI(PyInt_FromLong (v)); return *this; } }; // =============================================== // class Long class Long: public Object { public: // Constructor explicit Long (PyObject *pyob): Object (pyob) { validate(); } Long (const Long& ob): Object(*ob) { validate(); } // create from long explicit Long (long v = 0L): Object(FromAPI(PyLong_FromLong(v))) { validate(); } // create from int explicit Long (int v) { long w = v; p = PyLong_FromLong(w); validate(); } // try to create from any object explicit Long (const Object& ob): Object() { p = PyNumber_Long(*ob); validate(); } // Assignment acquires new ownership of pointer Long& operator= (const Object& rhs) { return (*this = *rhs); } Long& operator= (const PyObject* rhsp) { if(*this == rhsp) return *this; set (FromAPI(PyNumber_Long(const_cast(rhsp)))); return *this; } // Membership virtual bool accepts (PyObject *pyob) const { return pyob && PyLong_Check (pyob); } // convert to long operator long() const { return PyLong_AsLong (p); } operator double() const { return PyLong_AsDouble (p); } // assign from an int Long& operator= (int v) { *this = FromAPI(PyLong_FromLong (long(v))); return *this; } // assign from long Long& operator= (long v) { *this = FromAPI(PyLong_FromLong (v)); return *this; } }; // =============================================== // class Float // No constructor from a double since it is bad to overload on a pointer // and a numerical type. class Float: public Object { public: // Constructor explicit Float (PyObject *pyob): Object(pyob) { validate(); } Float (const Float& f): Object(f) { validate(); } // make from double explicit Float (double v=0.0) { p = PyFloat_FromDouble (v); validate(); } // try to make from any object explicit Float (const Object& ob): Object() { p = PyNumber_Float(*ob); validate(); } Float& operator= (const Object& rhs) { return (*this = *rhs); } Float& operator= (const PyObject* rhsp) { if(*this == rhsp) return *this; set (FromAPI(PyNumber_Float(const_cast(rhsp)))); return *this; } // Membership virtual bool accepts (PyObject *pyob) const { return pyob && PyFloat_Check (pyob); } // convert to double operator double () const { return PyFloat_AsDouble (p); } // assign from a double Float& operator= (double v) { *this = FromAPI(PyFloat_FromDouble (v)); return *this; } // assign from an int Float& operator= (int v) { *this = FromAPI(PyFloat_FromDouble (double(v))); return *this; } // assign from long Float& operator= (long v) { *this = FromAPI(PyFloat_FromDouble (double(v))); return *this; } // assign from an Int Float& operator= (const Int& iob) { *this = FromAPI(PyFloat_FromDouble (double(long(iob)))); return *this; } }; // class Sequence // ...the base class for all sequence types class Sequence: public Object { protected: explicit Sequence (): Object() {} public: // Items are the result of the [] and *iterator operations class Item protected: Sequence* s; // the sequence int offset; // item number public: Item (Sequence* seq, int i) : s(seq), offset(i) {}; ~Item() {} operator Object() const { return s->getItem (offset); } Item& operator=(const Item& rhs) { //lvalue s->setItem(offset, Object(rhs)); return *this; } Item& operator=(Object ob){ // lvalue s->setItem(offset, ob); return *this; } }; // end of Item class iterator; explicit Sequence (PyObject* pyob): Object(pyob) { validate(); } Sequence (const Sequence& ob): Object(ob) { validate(); } // Assignment acquires new ownership of pointer Sequence& operator= (const Object& rhs) { return (*this = *rhs); } Sequence& operator= (const PyObject* rhsp) { if(*this == rhsp) return *this; set (rhsp); return *this; } virtual bool accepts (PyObject *pyob) const { return pyob && PySequence_Check (pyob); } int length () const { return PySequence_Length (p); } // Element access const Item operator[](int index) const { return Item(const_cast(this), index); } Item operator[](int index) { return Item(const_cast(this), index); } virtual Object getItem (int i) const { return Object(FromAPI(PySequence_GetItem (p, i))); } virtual void setItem (int i, const Object& ob) { if (PySequence_SetItem (p, i, *ob) == -1) { throw PyException(); } } Sequence repeat (int count) const { Require (count >= 0); return Sequence (FromAPI (PySequence_Repeat (p, count))); } Sequence concat (const Sequence& other) const { return Sequence (FromAPI (PySequence_Concat(p, *other))); } class iterator { protected: Sequence* seq; int count; iterator (Sequence* s, int where): seq(s), count(where) {} public: ~iterator () {} bool operator!= (const iterator& other) { return (seq != other.seq) || (count != other.count); } Item operator*() { return Item(seq, count);} // prefix ++ iterator& operator++ () { count++; return *this;} // postfix ++ const iterator operator++ (int) { return iterator(seq, count++);} // prefix -- iterator& operator-- () { count--; return *this;} // postfix -- const iterator operator-- (int) { return iterator(seq, count--);} }; // end of class Sequence::iterator iterator begin () const { return iterator(const_cast(this), 0); } iterator end () const { return iterator(const_cast(this), length()); } }; // ================================================== // class String class String: public Sequence { public: virtual void setItem (int offset, PyObject* pitem) { throw PyException("Cannot assign to a String element."); } explicit String (PyObject *pyob): Sequence (pyob) { validate(); } String (const String& ob): Sequence(ob) { validate(); } // Create from CString String (const char *v = ""): Sequence(){ p = PyString_FromString (const_cast (v)); validate(); } String (const char *v, int vsize): Sequence() { p = PyString_FromStringAndSize (const_cast(v), vsize); validate(); } // Assignment acquires new ownership of pointer String& operator= (const Object& rhs) { return (*this = *rhs); } String& operator= (const PyObject* rhsp) { if(*this == rhsp) return *this; set (rhsp); return *this; } // Membership virtual bool accepts (PyObject *pyob) const { return pyob && PyString_Check (pyob); } // Convert to C string operator char* () const { return PyString_AsString (p); } // Assignment from C string String& operator= (const char *v) { *this = FromAPI(PyString_FromString (const_cast (v))); return *this; } // Append to present string void operator+= (const Sequence& s) { *this = concat(s); } void operator+= (const char* cs) { *this = concat(String(cs)); } // Replicate present string void operator*= (int n) { Require(n > 0); *this = repeat(n); } // Queries int size () const { // same as length as far as I know? return PyString_Size (p); } }; // ================================================== // class Tuple class Tuple: public Sequence { public: virtual void setItem (int offset, const Object&ob) { // note PyTuple_SetItem is a thief... if(PyTuple_SetItem (p, offset, new_reference_to(ob)) == -1) { throw PyException(); } } // Constructor explicit Tuple (PyObject *pyob): Sequence (pyob) { validate(); } Tuple (const Tuple& ob): Sequence(ob) { validate(); } // New tuple of a given size explicit Tuple (int size = 0): Sequence() { p = PyTuple_New (size); validate (); for (int i=0; i < size; i++) { if(PyTuple_SetItem (p, i, new_reference_to(Py_None)) == -1) { throw PyException(); } } } // Tuple from any sequence explicit Tuple (const Sequence& s) p = PyTuple_New (s.length()); validate(); for(int i=0; i < s.length(); i++) { if(PyTuple_SetItem (p, i, new_reference_to(s[i])) == -1) { throw PyException(); } } } // Assignment acquires new ownership of pointer Tuple& operator= (const Object& rhs) { return (*this = *rhs); } Tuple& operator= (const PyObject* rhsp) { if(*this == rhsp) return *this; set (rhsp); return *this; } // Membership virtual bool accepts (PyObject *pyob) const { return pyob && PyTuple_Check (pyob); } Tuple getSlice (int i, int j) const { return Tuple (FromAPI(PySequence_GetSlice (p, i, j))); } }; // ================================================== // class List class List: public Sequence public: // Constructor explicit List (PyObject *pyob): Sequence(pyob) { validate(); } List (const List& ob): Sequence(ob) { validate(); } // Creation at a fixed size List (int size = 0): Sequence() { p = PyList_New (size); validate(); for (int i=0; i < size; i++) { if(PyList_SetItem (p, i, new_reference_to(Py_None)) == -1) { throw PyException(); } } } // List from a sequence List (const Sequence& s): Sequence() { int n = s.length(); p = PyList_New (n); validate(); for (int i=0; i < n; i++) { if(PyList_SetItem (p, i, new_reference_to(s[i])) == -1) { throw PyException(); } } } // Assignment acquires new ownership of pointer List& operator= (const Object& rhs) { return (*this = *rhs); } List& operator= (const PyObject* rhsp) { if(*this == rhsp) return *this; set (rhsp); return *this; } // Membership virtual bool accepts (PyObject *pyob) const { return pyob && PyList_Check (pyob); } List getSlice (int i, int j) const { return List (FromAPI(PyList_GetSlice (p, i, j))); } void setSlice (int i, int j, const Object& v) { if(PyList_SetSlice (p, i, j, *v) == -1) { throw PyException(); } } void append (const Object& ob) { if(PyList_Append (p, *ob) == -1) { throw PyException(); } } void insert (int i, const Object& ob) { if(PyList_Insert (p, i, *ob) == -1) { throw PyException(); } } void sort () { if(PyList_Sort(p) == -1) { throw PyException(); } } void reverse () { if(PyList_Reverse(p) == -1) { throw PyException(); } } }; // class Mapping // ================================================== class Mapping: public Object { protected: explicit Mapping(): Object() { } public: // MappingItem: proxy class for implementing [] class MappingItem private: Mapping& s; const char *key; public: MappingItem (Mapping& seq, const char *k) : s(seq), key(k) {}; MappingItem& operator=(const MappingItem& rhs); //lvalue MappingItem& operator=(Object ob); // lvalue operator Object() const; // rvalue }; // end of MappingItem // Constructor explicit Mapping (PyObject *pyob): Object(pyob) { validate(); } Mapping (const Mapping& ob): Object(ob) { validate(); } // Assignment acquires new ownership of pointer Mapping& operator= (const Object& rhs) { return (*this = *rhs); } Mapping& operator= (const PyObject* rhsp) { if(*this == rhsp) return *this; set (rhsp); return *this; } // Membership virtual bool accepts (PyObject *pyob) const { return pyob && PyMapping_Check(pyob); } // Clear -- PyMapping Clear is missing // void clear () { List k = keys(); for(List::iterator i = k.begin(); i != k.end(); i++) { delItem(*i); } } // Element Access const MappingItem operator[](const char* key) const; MappingItem operator[](const char* key); int length () const { return PyMapping_Length (p); } int hasKey (const char* s) const { Require(s != 0); return PyMapping_HasKeyString (p, const_cast (s)); } Object getItem (const char* s) const { Require (s != 0); return Object( FromAPI( PyMapping_GetItemString (p,const_cast (s)) ) ); } void setItem (const char* s, const Object& ob) { if (PyMapping_SetItemString (p, const_cast (s), *ob) == -1){ throw PyException(); } } void delItem (const char* s) { Require (s != 0); if (PyMapping_DelItemString (p, const_cast (s)) == -1){ throw PyException(); } } void delItem (const Object& s) { if (PyMapping_DelItem (p, *s) == -1){ throw PyException(); } } // Queries List keys () const { return List(FromAPI(PyMapping_Keys(p))); } List values () const { // each returned item is a (key, value) pair return List(FromAPI(PyMapping_Values(p))); } List items () const { return List(FromAPI(PyMapping_Items(p))); } }; // ================================================== // class Dict class Dict: public Mapping { public: // Constructor explicit Dict (PyObject *pyob): Mapping (pyob) { validate(); } Dict (const Dict& ob): Mapping(ob) { validate(); } // Creation Dict (): Mapping() { p = PyDict_New (); validate(); } // Assignment acquires new ownership of pointer Dict& operator= (const Object& rhs) { return (*this = *rhs); } Dict& operator= (const PyObject* rhsp) { if(*this == rhsp) return *this; set(rhsp); return *this; } // Membership virtual bool accepts (PyObject *pyob) const { return pyob && PyDict_Check (pyob); } }; class Callable: public Object { protected: explicit Callable (): Object() {} public: // Constructor explicit Callable (PyObject *pyob): Object (pyob) { validate(); } Callable (const Callable& ob): Object(ob) { validate(); } // Assignment acquires new ownership of pointer Callable& operator= (const Object& rhs) { return (*this = *rhs); } Callable& operator= (const PyObject* rhsp) { if(*this == rhsp) return *this; set (rhsp); return *this; } // Membership virtual bool accepts (PyObject *pyob) const { return pyob && PyCallable_Check (pyob); } // Call Object apply(const Tuple& args) const { return Object(FromAPI(PyObject_CallObject(p, *args))); } Object apply(PyObject* args = Py_Null) const { return Object(FromAPI(PyObject_CallObject(p, args))); } }; Object operator+ (const Object& a); Object operator- (const Object& a); Object abs(const Object& a); pair coerce (Object& a, Object& b); Object operator+ (const Object& a, const Object& b); Object operator+ (const Object& a, int j); Object operator+ (const Object& a, double v); Object operator+ (int j, const Object& b); Object operator+ (double v, const Object& b); Object operator- (const Object& a, const Object& b); Object operator- (const Object& a, int j); Object operator- (const Object& a, double v); Object operator- (int j, const Object& b); Object operator- (double v, const Object& b); Object operator* (const Object& a, const Object& b); Object operator* (const Object& a, int j); Object operator* (const Object& a, double v); Object operator* (int j, const Object& b); Object operator* (double v, const Object& b); Object operator/ (const Object& a, const Object& b); Object operator/ (const Object& a, int j); Object operator/ (const Object& a, double v); Object operator/ (int j, const Object& b); Object operator/ (double v, const Object& b); Object operator% (const Object& a, const Object& b); Object operator% (const Object& a, int j); Object operator% (const Object& a, double v); Object operator% (int j, const Object& b); Object operator% (double v, const Object& b); } // end namespace Py #endif // __CXX_Objects__h _______________ C++-SIG - SIG for Development of a C++ Binding to Python send messages to: c++-sig at python.org administrivia to: c++-sig-request at python.org _______________ From furnish at xdiv.lanl.gov Thu Jan 15 22:21:18 1998 From: furnish at xdiv.lanl.gov (Geoffrey M. Furnish) Date: Thu, 15 Jan 1998 21:21:18 GMT Subject: [C++-SIG] C++ Framework In-Reply-To: <9801152044.AA20553@wrc.xerox.com> References: <9801152044.AA20553@wrc.xerox.com> Message-ID: <199801152121.VAA15951@twix.lanl.gov> Jeff Whitmire writes: > > My application runs mainly in python with help from 4-5 python > extensions that integrate third party libraries. We have just > finished a major release, and have the time to rework a bit of > this. I would love to rewrite these extensions in C++ using your > C++ bindings, but first I wanted to get some information from you, > if you have the time to answer a couple of quick questions. > > Have you tried to do anything with your bindings with respect to > 1.5? As a matter of fact, I have been working on that some today, and for the last couple of days. I am considering a reformulation of how the trampoline works. > Are they compatible with Visual C++ 5.0? (We run on Solaris and NT) I don't know, as I don't use that platform. > Are there any legal issues regarding using your C++ bindings in a > commercial application? (I know they are copyrighted, but I didn't > notice anything about reuse) Livermore has got its Python stuff out free. The stuff I did while I was there, is in the stock Livermore Python snapshot, on icf.llnl.gov. Los Alamos (where I am now), is just learning to discuss the issue, and I am not sure how long it will take to get resolution on the status of what I do sitting here. I did initiate inquiries into this matter a couple of days ago. > Do you have any sort of a status document that might show current > stability, known bugs, things left to do, etc.? Alas no, but I should. > Thanks for all the work you have already put into this project, and > I look forward to your response. I am hoping to get a non trivial project home page up soon, but it isn't ready yet. No ETA. -- Geoffrey Furnish email: furnish at lanl.gov LANL XTM Radiation Transport/POOMA phone: 505-665-4529 fax: 505-665-5538 "Software complexity is an artifact of implementation." -Dan Quinlan _______________ C++-SIG - SIG for Development of a C++ Binding to Python send messages to: c++-sig at python.org administrivia to: c++-sig-request at python.org _______________ From dubois1 at llnl.gov Thu Jan 15 23:00:12 1998 From: dubois1 at llnl.gov (Paul F. Dubois) Date: Thu, 15 Jan 1998 14:00:12 -0800 Subject: [C++-SIG] C++ Framework Message-ID: <01bd2200$f3e11200$998a7380@pduboispc> I'm about done with my CXX_Objects.h and CXX_Exceptions.h, which are (incompatible) replacements for the two similarly-named (experimental) PyXX_...hh files currently in the LLNL distribution. I called them something new so that I wouldn't bother Geoff's work. I'll try to get them out on our site next week. I sent out some of it for comment the other day and didn't get any. My stuff does work with MSVC++. I'm developing under it to make sure, in fact. Geoff's existing stuff won't work with MSVC, I'm sure. -----Original Message----- From: Geoffrey M. Furnish To: Jeff Whitmire Cc: Python/C++ SIG Date: Thursday, January 15, 1998 1:25 PM Subject: RE: [C++-SIG] C++ Framework >Jeff Whitmire writes: > > > > My application runs mainly in python with help from 4-5 python > > extensions that integrate third party libraries. We have just > > finished a major release, and have the time to rework a bit of > > this. I would love to rewrite these extensions in C++ using your > > C++ bindings, but first I wanted to get some information from you, > > if you have the time to answer a couple of quick questions. > > > > Have you tried to do anything with your bindings with respect to > > 1.5? > >As a matter of fact, I have been working on that some today, and for >the last couple of days. I am considering a reformulation of how the >trampoline works. > > > Are they compatible with Visual C++ 5.0? (We run on Solaris and NT) > >I don't know, as I don't use that platform. > > > Are there any legal issues regarding using your C++ bindings in a > > commercial application? (I know they are copyrighted, but I didn't > > notice anything about reuse) > >Livermore has got its Python stuff out free. The stuff I did while I >was there, is in the stock Livermore Python snapshot, on icf.llnl.gov. > >Los Alamos (where I am now), is just learning to discuss the issue, >and I am not sure how long it will take to get resolution on the >status of what I do sitting here. I did initiate inquiries into this >matter a couple of days ago. > > > Do you have any sort of a status document that might show current > > stability, known bugs, things left to do, etc.? > >Alas no, but I should. > > > Thanks for all the work you have already put into this project, and > > I look forward to your response. > >I am hoping to get a non trivial project home page up soon, but it >isn't ready yet. No ETA. > >-- >Geoffrey Furnish email: furnish at lanl.gov >LANL XTM Radiation Transport/POOMA phone: 505-665-4529 fax: 505-665-5538 > >"Software complexity is an artifact of implementation." -Dan Quinlan > >_______________ >C++-SIG - SIG for Development of a C++ Binding to Python > >send messages to: c++-sig at python.org >administrivia to: c++-sig-request at python.org >_______________ > _______________ C++-SIG - SIG for Development of a C++ Binding to Python send messages to: c++-sig at python.org administrivia to: c++-sig-request at python.org _______________ From busby at icf.llnl.gov Thu Jan 15 23:46:38 1998 From: busby at icf.llnl.gov (L. Busby) Date: Thu, 15 Jan 98 14:46:38 PST Subject: [C++-SIG] C++ Framework Message-ID: <9801152246.AA06317@icf.llnl.gov.llnl.gov> [ Among other things, Furnish said in response to Whitmire ] >> Are there any legal issues regarding using your C++ bindings in a >> commercial application? (I know they are copyrighted, but I didn't >> notice anything about reuse) > >Livermore has got its Python stuff out free. The stuff I did while I >was there, is in the stock Livermore Python snapshot, on icf.llnl.gov. It hasn't happened yet, but I believe the language of the LLNL copyright will change to include a paragraph similar to the following: LICENSING REQUIREMENTS Permission is hereby granted to use and copy this software and documentation for internal noncommercial purposes only, provided that 1) the above copyright notice and disclaimer appear in all copies of the software and documentation, and 2) all UC LLNL identification in the user interface remains unchanged. Any use, reproduction, modification, or distribution of this software or documentation for commercial purposes requires a license from the University. Contact: Lawrence Livermore National Laboratory, Industrial Partnerships and Commercialization Office, P.O. Box 808, L-795, Livermore, CA 94551." I haven't pursued the implications of that paragraph as it applies to the LLNL Python Modules, and don't intend to until I am directed to change the language in our release. It appears to contract the meaning of ``free'' substantially, however. _______________ C++-SIG - SIG for Development of a C++ Binding to Python send messages to: c++-sig at python.org administrivia to: c++-sig-request at python.org _______________ From marcelo at mds.rmit.edu.au Thu Jan 22 06:35:15 1998 From: marcelo at mds.rmit.edu.au (Marcelo Cantos) Date: Thu, 22 Jan 1998 16:35:15 +1100 (EST) Subject: [C++-SIG] Newcomer Message-ID: <199801220535.QAA06440@io.mds.rmit.edu.au> As a newcomer to the list I'd like to know what the state of affairs is at present and, more importantly, what state sources are in and, even more importantly, what work most needs doing at present. Cheers, Marcelo _______________ C++-SIG - SIG for Development of a C++ Binding to Python send messages to: c++-sig at python.org administrivia to: c++-sig-request at python.org _______________ From dubois1 at llnl.gov Thu Jan 22 20:45:46 1998 From: dubois1 at llnl.gov (Paul F. Dubois) Date: Thu, 22 Jan 1998 11:45:46 -0800 Subject: [C++-SIG] Extension modules and objects in C++ Message-ID: <01bd276e$55340400$998a7380@pduboispc> Here is something I wanted to run by the SIG for comments. I've been working on trying to make it less weird to create extension modules and extension objects. Here is the init function for a sample module and sample object under this revised scheme. See if you think this is less weird or more weird. extern "C" { void initexample() { // experimental initialization stuff init_rtype(); // initialize the object type "r" ExtensionModule example("example"); example.add("sum", ex_sum, "sum(arglist) = sum of arguments"); example.add("test", ex_test, "test(arglist) runs a test suite"); example.add("r", ex_r_new, "r(start,stop,stride)"); Dict d = example.initialize(); d["a_constant"] = Int(10); } } The resulting module has three methods "sum", "test", and "r", and a constant "a_constant" which is a Python int = 10. Here is the definition of the extension type for which the method "r" is the constructor. r implements sequence length and item, and has a repr function. The declaration PythonType declares a parameterized class using the structure in question as a parameter. // This part is experimental... typedef struct { PyObject_HEAD ... some data members.... } r; static PythonType rtype("r"); PyObject * r_New(long start, long stop, long step=1) { r *robj = PyObject_NEW(r, rtype.method_table()); ... initialize the data members.... return reinterpret_cast (robj); } PyObject* ex_r_new (PyObject* self, PyObject* args) { ... details omitted, but it ends up calling r_New... } static void r_dealloc(r *robj) { PyMem_DEL(robj); } static PyObject * r_repr(r *robj) { ...details omitted... } static int r_length(r* robj) { ...details omitted } static PyObject* r_item(r* robj, int i) { details omitted... } static void init_rtype () { rtype.doc("r objects: blah, blah, blah"); rtype.dealloc(r_dealloc); rtype.repr(r_repr); rtype.sequence_length(r_length); rtype.item(r_item); } _______________ C++-SIG - SIG for Development of a C++ Binding to Python send messages to: c++-sig at python.org administrivia to: c++-sig-request at python.org _______________ From furnish at xdiv.lanl.gov Thu Jan 22 21:10:23 1998 From: furnish at xdiv.lanl.gov (Geoffrey M. Furnish) Date: Thu, 22 Jan 1998 20:10:23 GMT Subject: [C++-SIG] Newcomer In-Reply-To: <199801220535.QAA06440@io.mds.rmit.edu.au> References: <199801220535.QAA06440@io.mds.rmit.edu.au> Message-ID: <199801222010.UAA05745@twix.lanl.gov> Marcelo Cantos writes: > As a newcomer to the list I'd like to know what the state of affairs > is at present and, more importantly, what state sources are in and, > even more importantly, what work most needs doing at present. A great question. First off, I should say that there are more than one person/group working on various aspects of binding Python to C++. I can only speak for myself, and to some extent, Paul Dubois. Back in the 1.4 days, we had achieved the state where Python's configure script had been hacked to include a --with-cxx option, so that you could specify a C++ compiler. If one was specified, it was used to compile main. This was so that global ctors would get run, and is a practical necessity. With 1.5, and it's new, simpler build-by-linking-to-libPython strategy, this is not the issue it once was. Users can fairly easily copy the 3 line main to their home dir, and just compile it with C++. So I am thinking that perhaps with 1.5, we don't necessarily need the hacks to configure.in. I do think there will be some issues here for Guido to ponder if he does decide to integrate the produce of this sig into the mainline release at some point. It certainly won't do to have a Python which links or dynloads C++ extensions, but which wasn't linked with a C++ compiler. But that's for Guido to sort out later. For now, I think we can get by without hacking configure.in, and just let people compile the 3 line main themselves. Now, onto method invocation. While I was at LLNL, I was able to construct a system that allowed you to get a python handle to a C++ object, and invoke C++ methods "directly" from the Python interpreter. For example, given: class foo /* details omitted */ public: PyObject *meth1( PyArgs *args ); }; you could do something like: f = new_foo() f.meth1( ) and control flow would show up in foo:meth1() forthwith. Now, in that code (which is available via the LLNL Python distribution), foo had to be derived from PyObject, and PyObject had been hacked to be a class with a virtual destructor, so that the method invocation machinery could do RTTI downcasts. There are problems with this approach. Some people had expressed the desire to compose Python classes which "wrapped" preexisting classes. So, you might want to have something like this: // The orriginal article. class foo { public: void meth1( int x ); }; // A python wrapper class which calls the real thing, but pads it // going in and out, for the purpose of marshalling arguments and // return types to/from Python. class Pyfoo : public PyObject { foo *pfoo; public: PyObject *meth1( PyArgs *args ) { int x = ; pfoo->meth1( x ); PyINCREF( PyNone ); return PyNone; } }; which is okay as far as it goes. Constructing Pyfoo from foo could be a conceivably automatable process. The problem is that if foo (the orriginal class), had ancestors, then constructing the Python wrapper class got hairy, because of the need for the wrapper to inherit from PyObject. If you wanted to build a wrapper for something which used inheritance, you might like to have a family of wrappers which bear the same relationship to each other as the original class you are wrapping bear to themselves. But now you would wind up with the PyObject getting stuffed into the inheritance hierarchy of the shadow class at multiple points, and that got real ugly. I could never quite get the down casting code to do every case perfectly. Plus, each of the shadow classes would have a pointer to its "slice" of the shadowed class, and ownership gets tricky, etc. All in all this scheme seemed to present thorny issues for this particularly straightforward prescription for building shadow class hierarchies. I had come up with a plan for a reformulation just in the closing days of my term at LLNL, but didn't get it done before leaving, and have had a range of other tasks here at LANL which delayed me in doing this until I had forgotten the details of the plan. However, I have recently been revitalizing the C++ extension stuff for use in Python 1.5 for work here, and so am starting to fuss with it some more. My current thinking is to ditch the required inheritance from PyObject, as well as the hack to make PyObject a class with a vtbl. I am worried that this will have bad effects which just hadn't shown up yet. Instead, I am thinking it may make more sense to just build a custom trampoline for each wrapped class (using templates), so that the type of the wrapped (registered) class can be recovered with a static cast instead of a dynamic cast. Or maybe if we are lucky, we might be able to avoid any casts... I should say that I am not actuallly 100% sure this will work. I will have to code it up to become positive, but I am hopeful. If it does work, it should help simplify the business of building shadow class wrappers. In any event, however the call is made, it is wrapped in a try/catch clause, so that C++ exceptions can be converted to Python exceptions. This is pretty nice. Then there is the portion of the project that relates to wrapping the Python C API in suitably constituted C++ classes. The idea here is that classes will be produced which correspond to the various functional categories of the Python C API. A Dict class, for example. Then, instead of calling the C API, you could instantiate a Python Dict object, and make calls to it, which would vector to the appropriate Python C API functions, but would have conventional C++ object semantics, maybe even look STLish, etc. And in this wrapper layer, any errors in the Python C API would be converted directly to C++ exceptions, which could be caught and handled using conventional C++ exception handling techniques. So, in this model, C++ use of the Python API would be much cleaner because you wouldn't have to do all this error code handling like you have to do when you call the Python C API from C. You would just use standard C++ exceptio9n handling, which cleans up the usage model dramatically. Paul Dubois has been working on this part of the wrapping, and that work is continuing to progress. As for what needs doing... Well, first of all, you give us feedback on how well this agenda meets your needs. Maybe there's things you need that aren't covered here. In terms of actually writing code, perhaps you could volunteer to help Paul wrap portions of the Python C API that he hasn't gotten to yet, or help write test software, or something like that. I am hoping to put together a 1.5 compatible release of what we have so far, sometime in the near future, where "near" is ill defined. -- Geoffrey Furnish email: furnish at lanl.gov LANL XTM Radiation Transport/POOMA phone: 505-665-4529 fax: 505-665-5538 "Software complexity is an artifact of implementation." -Dan Quinlan _______________ C++-SIG - SIG for Development of a C++ Binding to Python send messages to: c++-sig at python.org administrivia to: c++-sig-request at python.org _______________ From dubois1 at llnl.gov Thu Jan 22 22:43:06 1998 From: dubois1 at llnl.gov (Paul F. Dubois) Date: Thu, 22 Jan 1998 13:43:06 -0800 Subject: [C++-SIG] a little braver version Message-ID: <01bd277e$b91eadc0$998a7380@pduboispc> If one is willing to use templated functions, I actually have this working. I omit the definitions of the methods. Note that r_dealloc is not defined any more; the appropriate default is supplied that does the PyMem_DEL. typedef struct { PyObject_HEAD ... data ... } r; r * r_new(...appropriate args...) { // no more remembering how to call PyObject_NEW... r *robj = constructor(); ... set the data members... } ...defn's of methods same as before... ...but rtype not defined as a static static void init_rtype () { PythonType& rtype = type_object (); rtype.name("r"); rtype.doc("r objects: blah,blah"); rtype.repr(r_repr); rtype.sequence_length(r_length); rtype.item(r_item); } _______________ C++-SIG - SIG for Development of a C++ Binding to Python send messages to: c++-sig at python.org administrivia to: c++-sig-request at python.org _______________ From dubois1 at llnl.gov Fri Jan 23 17:11:19 1998 From: dubois1 at llnl.gov (Paul F. Dubois) Date: Fri, 23 Jan 1998 08:11:19 -0800 Subject: [C++-SIG] Anybody know all the rules for extension objects? Message-ID: <01bd2819$8a371100$998a7380@pduboispc> There are some rules for type objects that I'm not sure are written down anywhere. For example, a. Every type must have a dealloc function. b. If a type defines a number add and is also a sequence you must define sequence concat. Failure to obey these rules results in code crashes of the unpleasant sort. In a C++ facility to make it easier to write correct extensions to Python, I can fix a few of these problems. For example, in case (a) I can supply a default dealloc and in case (b) set the concat to be the same as the add if the concat is not supplied. So, what are the rest of the rules for type objects? _______________ C++-SIG - SIG for Development of a C++ Binding to Python send messages to: c++-sig at python.org administrivia to: c++-sig-request at python.org _______________ From dheise at metronet.de Thu Jan 22 23:49:47 1998 From: dheise at metronet.de (Dirk Heise) Date: Thu, 22 Jan 1998 23:49:47 +0100 Subject: [C++-SIG] Newcomer Message-ID: <19980123201908.29996.qmail@braunschweig2.pop.metronet.de> Geoffrey M. Furnish wrote: As i'm new too, this helped me a lot, thanks, Geoffrey. I'd like to suggest to send this text to every new subscriber so they'll get an idea immediately (i subscribed to a genetic programming list and they have a very good text they send to every new subscriber, i found this very helpful.) But i have one further question: To have python shadow classes that wrap C++ classes, one can use SWIG. Doesn't it make more sense to extend the SWIG shadow class concept or its C++ parsing capabilities (if necessary) than writing your PyObject-derived C++ classes? I don't see the advantage. In Python, you do not need to model a class hierarchy of wrapper classes to resemble the inheritance of the wrapped C++ classes. Every wrapper class can be solo. Excuse me if this sounds blasphemic, i just wanna know what's the advantage of the C++ binding compared to SWIGging, practically? Dirk Heise, Braunschweig, Germany dheise at metronet.de _______________ C++-SIG - SIG for Development of a C++ Binding to Python send messages to: c++-sig at python.org administrivia to: c++-sig-request at python.org _______________ From beazley at sol.cs.utah.edu Fri Jan 23 23:27:14 1998 From: beazley at sol.cs.utah.edu (David Beazley) Date: Fri, 23 Jan 1998 15:27:14 -0700 Subject: [C++-SIG] Newcomer In-Reply-To: <19980123201908.29996.qmail@braunschweig2.pop.metronet.de> References: <19980123201908.29996.qmail@braunschweig2.pop.metronet.de> Message-ID: <199801232227.PAA12605@sol.cs.utah.edu> Dirk Heise writes: > > But i have one further question: > To have python shadow classes that wrap C++ classes, > one can use SWIG. > Doesn't it make more sense to extend the SWIG > shadow class concept or its C++ parsing capabilities > (if necessary) than writing > your PyObject-derived C++ classes? I don't see > the advantage. In Python, you do not need to > model a class hierarchy of wrapper classes to > resemble the inheritance of the wrapped C++ > classes. Every wrapper class can be solo. > > Excuse me if this sounds blasphemic, > i just wanna know what's the advantage of the C++ > binding compared to SWIGging, practically? > My impression (which may be wrong), is that the C++ binding effort is trying to come up with a more C++ friendly interface to Python that allows things like the ability to sub-class Python types, handle exceptions, and other things like that. Using this, it would presumably be easier to write C++ extension modules to Python (or to write extension modules in an entirely different manner than before). SWIG is mainly designed to automatically build Python extensions out of existing C/C++ code. I personally see SWIG as being entirely different than the C++ binding effort (in that SWIG doesn't really care what the C/C++ interface to Python is as long as there is one). However, I could see SWIG being used as a tool to automatically wrap C++ classes into Python extensions using the C++ binding. To my knowledge, no one has tried modifying SWIG to do this, but it would be possible in principle. As for a comparision between the C++ binding and SWIG shadow classes, I don't have any first-hand experience with that. However, if the C++ binding provides a similar capability, the main difference would probably be a performance improvement (the C++ binding would presumably be faster). Cheers, Dave _______________ C++-SIG - SIG for Development of a C++ Binding to Python send messages to: c++-sig at python.org administrivia to: c++-sig-request at python.org _______________ From esikes at anet-dfw.com Mon Jan 26 04:35:42 1998 From: esikes at anet-dfw.com (esikes at anet-dfw.com) Date: Sun, 25 Jan 1998 21:35:42 -0600 (CST) Subject: [C++-SIG] Home Bussiness Message-ID: A non-text attachment was scrubbed... Name: not available Type: text/plain charset=us-ascii Size: 840 bytes Desc: not available URL: From guido at cnri.reston.va.us Mon Jan 26 17:34:57 1998 From: guido at cnri.reston.va.us (Guido van Rossum) Date: Mon, 26 Jan 1998 11:34:57 -0500 Subject: [C++-SIG] Newcomer In-Reply-To: Your message of "Thu, 22 Jan 1998 23:49:47 +0100." <19980123201908.29996.qmail@braunschweig2.pop.metronet.de> References: <19980123201908.29996.qmail@braunschweig2.pop.metronet.de> Message-ID: <199801261634.LAA09815@eric.CNRI.Reston.Va.US> > I'd like to suggest to send this text to every new subscriber so > they'll get an idea immediately (i subscribed to a > genetic programming list and they have a very good text > they send to every new subscriber, i found this very > helpful.) Wouldn't it be better to place such an introductory text on the SIG's web page? That will inform people even *before* they subscribe... Surely in this day and age we can assume people have access to the web? I know the logistics of getting a SIG's web page are a little more involved than they ought to be -- our webmaster, Ken Manheimer, has set up a nice scheme so SIG owners can update their own web pages but it isn't always convenient. However I promise that if you mail whatever you want to be put up there to webmaster at python.org (or to me) it will be dealt with. --Guido van Rossum (home page: http://www.python.org/~guido/) _______________ C++-SIG - SIG for Development of a C++ Binding to Python send messages to: c++-sig at python.org administrivia to: c++-sig-request at python.org _______________