controlling types over the C++/Python interface
Hi,
I'm starting to explore the use of boost.python in cctbx. I've written a
simple C++ class and exposed its accessor methods as attributes using the
.add_property syntax in the wrapper. In the C++ class my member data and
accessor return types are, for example, scitbx::vec3<double>. When I access
the attribute in Python with the usual dot operator I get a tuple. What I'd
really like though is an object of type
Hi David, When I access the attribute in Python with the usual dot operator I get a
tuple. What I'd really like though is an object of type
, which is what I passed to the constructor of the object in Python.
This would be nice indeed but is a little tricky to implement. The C++ wrapper code would need to import scitbx.matrix. It's entirely possible but I always shied away from introducing this dependency.
I found I had to add
from cctbx.array_family import flex
to the package __init__.py where my C++ extension is imported,
This import piggy-backs a whole bunch of loosely related from-and-to-tuple conversions; see scitbx/array_family/boost_python/flex_ext.cpp. I had long discussions with Luc about this when he started using cctbx, because from a purists standpoint centralizing all the conversions in flex_ext.cpp breaks modularity. However, the discussions didn't lead to practical alternatives, and I believe the current solution works well in practice. Ralf
Hi Ralf,
Thanks for the explanation. it's good to know what I shouldn't try to solve
a problem, as I now feel more freely able to get on with solving it :) In
this case the extra overhead of creating scitbx.matrix entities from the
returned tuples will disappear with time, as more code gets moved to C++.
So I must keep in mind not to worry about performance at the Python level.
I have a better feel for the type mappings now I think. Tuple as a common
type for all sequence-type objects makes sense in Python, and as long as I
can expect to get tuples back I can avoid any nasty surprises.
-- David
On 14 May 2012 15:15, Ralf Grosse-Kunstleve
Hi David,
When I access the attribute in Python with the usual dot operator I get a
tuple. What I'd really like though is an object of type
, which is what I passed to the constructor of the object in Python. This would be nice indeed but is a little tricky to implement. The C++ wrapper code would need to import scitbx.matrix. It's entirely possible but I always shied away from introducing this dependency.
I found I had to add
from cctbx.array_family import flex
to the package __init__.py where my C++ extension is imported,
This import piggy-backs a whole bunch of loosely related from-and-to-tuple conversions; see scitbx/array_family/boost_python/flex_ext.cpp. I had long discussions with Luc about this when he started using cctbx, because from a purists standpoint centralizing all the conversions in flex_ext.cpp breaks modularity. However, the discussions didn't lead to practical alternatives, and I believe the current solution works well in practice.
Ralf
_______________________________________________ cctbxbb mailing list [email protected] http://phenix-online.org/mailman/listinfo/cctbxbb
Hi David,
Tuple as a common type for all sequence-type objects makes sense in Python
Only for those sequences whose size is fixed at compile-time, i.e. af::tiny, af::small and any type derived from them, which at the moment is limited to scitbx::vec3 and scitbx::vec2 from the top of my head. If you return an array with a dynamic size, i.e. af::shared or af::versa, then it will be converted to scitbx.array_family.shared and scitbx.array_family.flex respectively. HtH, Luc
Thanks for the tip-off! That does make sense. Is this documented anywhere,
as if so I didn't find it? Although no doubt I would have found out
eventually by experience.
Cheers,
David
On 15 May 2012 05:29, "Luc Bourhis"
Hi David,
Tuple as a common type for all sequence-type objects makes sense in Python
Only for those sequences whose size is fixed at compile-time, i.e. af::tiny, af::small and any type derived from them, which at the moment is limited to scitbx::vec3 and scitbx::vec2 from the top of my head. If you return an array with a dynamic size, i.e. af::shared or af::versa, then it will be converted to scitbx.array_family.shared and scitbx.array_family.flex respectively.
HtH,
Luc
_______________________________________________ cctbxbb mailing list [email protected] http://phenix-online.org/mailman/listinfo/cctbxbb
Thanks for the tip-off! That does make sense. Is this documented anywhere, as if so I didn't find it? Although no doubt I would have found out eventually by experience.
The following overview covers the dynamic as well as the static arrays: http://cci.lbl.gov/~hohn/array-family-tour.html In a few days, it will have become second nature! Luc
Looking at some cctbx C++ code, I note use of syntax like int i = 1234; double d = static_cast<double>(i); How is this different or indeed preferable to double d = double(i); or d = i; ? Phil
Hi Phil, A lot of people have this question, and it does seem unnecessary when a C-style cast (e.g. double d = double(i);) is easier. The FAQ at http://www2.research.att.com/~bs/bs_faq2.html#static-cast gives some of the reasons. The main two are: C style casts are vague -- there are a few logically different types of casting, and the C style cast doesn't really provide the compiler with enough information about what sort of cast is to be performed. The other reason that I hear a lot is, to quote the FAQ: "An ugly operation should have an ugly syntactic form." C style casts are next to impossible to search for, and they blend in when visually inspecting code. The C++ casting methods stick out like sore thumbs, as they should ;-) Hope this helps, Dave On Tue, May 15, 2012 at 4:17 AM, Phil Evans
wrote:Looking at some cctbx C++ code, I note use of syntax like
int i = 1234; double d = static_cast<double>(i);
How is this different or indeed preferable to
double d = double(i);
or
d = i;
?
Phil _______________________________________________ cctbxbb mailing list [email protected] http://phenix-online.org/mailman/listinfo/cctbxbb
Hi Phil,
Looking at some cctbx C++ code, I note use of syntax like
int i = 1234; double d = static_cast<double>(i);
How is this different or indeed preferable to
double d = double(i);
or
d = i;
?
Short answer: For conversions between non-pointer types, it does not matter but for pointer types, static_cast shall always be used to the exclusion of any other conversion.
The language lawyer:
The construct
double d = double(i)
which is known as functional-style cast is completely equivalent to the so-call cast notation, which comes from C:
double d = (double)i
You may see both in a few places in the cctbx code actually.
The problem with those two constructs is that the compiler will let use convert a pointer to anything into a pointer to anything else. Consider:
#include <iostream>
struct foo {
int i,j;
foo(int i, int j): i(i), j(j) {}
};
struct bar {
double d;
bar(double d): d(d) {}
};
int main() {
foo x(1, 2);
bar *y = (bar *)&x;
std::cout << y->d << std::endl;
return 0;
}
This prints 4.24399e-314 on my computer but this depends on the compiler. On the contrary,
bar *y = static_cast
I can see that problem with pointers but it doesn't seem to apply to double I am naive about this, I suppose 1) I never learnt C, just C++ 2) I don't use pointers (or very rarely) 3) I read "double(i)" as a constructor of a double from an int ie class double { public: double(const int& i); }; thus it shouldn't accept eg thing* instance; d = double(instance); which should be a compiler error, just as an illegal construction of any other object should be and indeed double* pd; double d = double(pd); gives t.cpp:4:23: error: invalid cast from type 'double*' to type 'double' Phil On 15 May 2012, at 11:38, Luc Bourhis wrote:
Hi Phil,
Looking at some cctbx C++ code, I note use of syntax like
int i = 1234; double d = static_cast<double>(i);
How is this different or indeed preferable to
double d = double(i);
or
d = i;
?
Short answer: For conversions between non-pointer types, it does not matter but for pointer types, static_cast shall always be used to the exclusion of any other conversion.
The language lawyer:
The construct
double d = double(i)
which is known as functional-style cast is completely equivalent to the so-call cast notation, which comes from C:
double d = (double)i
You may see both in a few places in the cctbx code actually.
The problem with those two constructs is that the compiler will let use convert a pointer to anything into a pointer to anything else. Consider:
#include <iostream>
struct foo { int i,j; foo(int i, int j): i(i), j(j) {} };
struct bar { double d; bar(double d): d(d) {} };
int main() { foo x(1, 2); bar *y = (bar *)&x; std::cout << y->d << std::endl; return 0; }
This prints 4.24399e-314 on my computer but this depends on the compiler. On the contrary,
bar *y = static_cast
(&x) is not legit and the compiler will bug you.
HtH,
Luc
_______________________________________________ cctbxbb mailing list [email protected] http://phenix-online.org/mailman/listinfo/cctbxbb
I can see that problem with pointers but it doesn't seem to apply to double [...]
You are perfectly right and I do prefer double(i) as well as it is less noisy. In template class or function, where a template parameter T will end up being a floating point type, you will see T(0) or T(1) a lot in the cctbx corpus, as a way to denote those constant in a way that is generic e.g. ("0." or "1." would not do as those denotes double and this may not work when instantiating the template for T=float). Too many people have been brainwash by the diktat: "you shall always use static_cast"! In any case, as I stated in my short answer, it is a matter of taste whether you write static_cast<T>(obj), (T)obj or T(obj) when T is not a pointer, and stylistic issues are only very loosely enforced among cctbx developers. What matters is that the compiler will actually prevent mistakes: consider my example again but let's do the conversion without the pointers: bar y = (bar)x; My compiler fails with the following error messages conv.cpp:15:11: error: no matching conversion for C-style cast from 'foo' to 'bar' bar y = (bar)x; ^~~~~~ conv.cpp:8:8: note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'foo' to 'const bar' for 1st argument; struct bar { ^ conv.cpp:10:3: note: candidate constructor not viable: no known conversion from 'foo' to 'double' for 1st argument; bar(double d): d(d) {} which tells you the whole story! Luc
Hi Phil, The main reasons for me to prefer static_cast is clarity; I just want to make the type conversion easily noticeable. Ralf On Tue, May 15, 2012 at 3:59 AM, Phil Evans
wrote:I can see that problem with pointers but it doesn't seem to apply to double
I am naive about this, I suppose
1) I never learnt C, just C++ 2) I don't use pointers (or very rarely) 3) I read "double(i)" as a constructor of a double from an int ie
class double { public: double(const int& i); };
thus it shouldn't accept eg
thing* instance; d = double(instance);
which should be a compiler error, just as an illegal construction of any other object should be
and indeed double* pd; double d = double(pd);
gives t.cpp:4:23: error: invalid cast from type 'double*' to type 'double'
Phil
On 15 May 2012, at 11:38, Luc Bourhis wrote:
Hi Phil,
Looking at some cctbx C++ code, I note use of syntax like
int i = 1234; double d = static_cast<double>(i);
How is this different or indeed preferable to
double d = double(i);
or
d = i;
?
Short answer: For conversions between non-pointer types, it does not matter but for pointer types, static_cast shall always be used to the exclusion of any other conversion.
The language lawyer:
The construct
double d = double(i)
which is known as functional-style cast is completely equivalent to the so-call cast notation, which comes from C:
double d = (double)i
You may see both in a few places in the cctbx code actually.
The problem with those two constructs is that the compiler will let use convert a pointer to anything into a pointer to anything else. Consider:
#include <iostream>
struct foo { int i,j; foo(int i, int j): i(i), j(j) {} };
struct bar { double d; bar(double d): d(d) {} };
int main() { foo x(1, 2); bar *y = (bar *)&x; std::cout << y->d << std::endl; return 0; }
This prints 4.24399e-314 on my computer but this depends on the compiler. On the contrary,
bar *y = static_cast
(&x) is not legit and the compiler will bug you.
HtH,
Luc
_______________________________________________ cctbxbb mailing list [email protected] http://phenix-online.org/mailman/listinfo/cctbxbb
_______________________________________________ cctbxbb mailing list [email protected] http://phenix-online.org/mailman/listinfo/cctbxbb
Fair enough My naive question arose from surprise I suppose. The constructor syntax seems natural, and as a non-C programmer the C-cast syntax "(double)i" looks weird thanks Phil On 16 May 2012, at 07:01, Ralf Grosse-Kunstleve wrote:
Hi Phil, The main reasons for me to prefer static_cast is clarity; I just want to make the type conversion easily noticeable. Ralf
On Tue, May 15, 2012 at 3:59 AM, Phil Evans
wrote:I can see that problem with pointers but it doesn't seem to apply to double
I am naive about this, I suppose
1) I never learnt C, just C++ 2) I don't use pointers (or very rarely) 3) I read "double(i)" as a constructor of a double from an int ie
class double { public: double(const int& i); };
thus it shouldn't accept eg
thing* instance; d = double(instance);
which should be a compiler error, just as an illegal construction of any other object should be
and indeed double* pd; double d = double(pd);
gives t.cpp:4:23: error: invalid cast from type 'double*' to type 'double'
Phil
On 15 May 2012, at 11:38, Luc Bourhis wrote:
Hi Phil,
Looking at some cctbx C++ code, I note use of syntax like
int i = 1234; double d = static_cast<double>(i);
How is this different or indeed preferable to
double d = double(i);
or
d = i;
?
Short answer: For conversions between non-pointer types, it does not matter but for pointer types, static_cast shall always be used to the exclusion of any other conversion.
The language lawyer:
The construct
double d = double(i)
which is known as functional-style cast is completely equivalent to the so-call cast notation, which comes from C:
double d = (double)i
You may see both in a few places in the cctbx code actually.
The problem with those two constructs is that the compiler will let use convert a pointer to anything into a pointer to anything else. Consider:
#include <iostream>
struct foo { int i,j; foo(int i, int j): i(i), j(j) {} };
struct bar { double d; bar(double d): d(d) {} };
int main() { foo x(1, 2); bar *y = (bar *)&x; std::cout << y->d << std::endl; return 0; }
This prints 4.24399e-314 on my computer but this depends on the compiler. On the contrary,
bar *y = static_cast
(&x) is not legit and the compiler will bug you.
HtH,
Luc
_______________________________________________ cctbxbb mailing list [email protected] http://phenix-online.org/mailman/listinfo/cctbxbb
_______________________________________________ cctbxbb mailing list [email protected] http://phenix-online.org/mailman/listinfo/cctbxbb
_______________________________________________ cctbxbb mailing list [email protected] http://phenix-online.org/mailman/listinfo/cctbxbb
participants (5)
-
David Lonie
-
David Waterman
-
Luc Bourhis
-
Phil Evans
-
Ralf Grosse-Kunstleve