A good way to implement mutual conversion of user-defined classes?
-
I have some user-defined classes:
class tMyType1{...}; class tMyType2{...}; class tMyType3{...}; class tMyType4{...}; class tMyType5{...};And I need this classes to be converted to each other.
First of all, the conversion routines should take additional conversion parameters. So, they cannot be implemented as
operator=. Another thing is that conversion is logically independent of classes implementation: classes should not know about each other. I decided to make conversion functions in the separate .h-file.So, I need your advice. What is a good practice for that?
I started to do this way:
//Conversion function template for the same types (5 conversions is one template): template<class T> inline void Convert(T const &vInput, T &vOutput, int additionParameter) { vOutput = vInput; } //Conversions for different types (20 conversions): inline void Convert(tMyType1 const &vInput, tMyType2 &vOutput, int additionParameter) { ... } inline void Convert(tMyType1 const &vInput, tMyType3 &vOutput, int additionParameter) { ... } ... inline void Convert(tMyType5 const &vInput, tMyType4 &vOutput, int additionParameter) { ... }The problem is that I need to write implementation for 20 functions. But in my case the types
tMyType4andtMyType5are very similar. The only difference is thattMyType4hasfloats inside, whiletMyType5hasdoubles. Therefore, the code for processingtMyType4andtMyType5values is identical. So, I need to write only 10 (not 20) different conversion functions.But how to make this? I need to write something like that:
template<class T = tMyType4 or tMyType5> inline void Convert(tMyType1 const &vInput, T &vOutput) { ... }Of course in that case I can write just this:
template<class T> inline void Convert(tMyType1 const &vInput, T &vOutput) { ... }and hope that
TistMyType4ortMyType5. (I can add static assert for additional security), but what to do, if I will have more than one pair (or tripple) of almost-identical types?
-
boost::enable_if<>
would work.But I think you are doing something wrong.
All these classes need to have something in common - otherwise a conversation won't make much sense... Often it is possible to implement all conversations based on a generic interface. For example, you said some classes use float, some uses double. That's the same Code. But what do the others use? int? string? etc. All can be converted easily with some simple helper functions like boost::lexical_cast<>.
-
You can write "read" and "write" methods for each class to de/serialize them as XML (or some simpler format), then you can save one class as XML and load another from this XML.
-
I'd like to know what those types are. Without that knowledge one can only speculate as to what would be the best way to do things.
-
Shade Of Mine schrieb:
... conversation ...
Pretty sure you meant conversions?
Besides, I totally agree.
-
hustbaer schrieb:
I'd like to know what those types are. Without that knowledge one can only speculate as to what would be the best way to do things.
You are right. I need to convert colors from one color space to another. This needs to be done very fast.
Color is something like that:
template<idColorFormat CF, idColorComponentType CT> struct tColor;idColorFormatis something likecfRGB,cfBGR,cfGrayscale,cfIndexed.
idColorComponentTypeis something likectUChar,ctUShort,ctUInt,ctFloat,ctDouble.I have already done all possible conversions of colors from one pair of parameters
<CF,CT>to any other pair. The idea is thattColorcontain 3 or 1 components inside:template<idColorComponentType CT> tColorComponent; template<idColorComponentType CT> tColor<cfRGB,CT> { tColorComponent<CT> r,g,b; ... };So, the conversion operators of
tColordeal with differentidColorFormat, whiletColorComponentdeal with differentidColorComponentType.So (if omit
cfIndexed), I need (3*3-3)+(5*5-5) = 6+20 conversion operators. And I have done this.The problem is that I need to go further, and make color space conversion. I decided to use ONLY
cfRGBcolor format for such conversions. Color space is something like that:enum idColorSpace{csCIEXYZ, //CIE 1931 XYZ color space. X,Y,Z values are stored in R,G,B respectively csSRGBLinear, //sRGB Linear color space csSRGB, //sRGB color space csCIELAB}; //CIE 1976 LAB color space. L,A,B values are stored in G,R,B respectively template<idColorSpace T> struct tColorSpace{}; //To store idColorSpace as compile-time constant to use function overloading instead of template specialization.I think conversion functions should look like that:
template<idColorComponentType CT1, idColorComponentType CT2, idColorSpace CS1, idColorSpace CS2> inline void ConvertColor(tColor<cfRGB,CT1> const &c1, tColor<cfRGB,CT2> &c2, tColorSpace<CS1>, tColorSpace<CS2>, bool clip);Notice that I perform color space conversion at the same time as color component type conversion. This is very important to be able to do some optimizations (such as using of tables).
-
SAn schrieb:
But how to make this? I need to write something like that:
template<class T = tMyType4 or tMyType5> inline void Convert(tMyType1 const &vInput, T &vOutput) { ... }you could use a traits-template (like std::iterator_traits).
-
example:
class CA {}; class CB {}; class CC {}; class CD {}; class CE {}; namespace hidden { struct float_cat {}; struct other_cat {}; // assign categories (concepts) to the userdefined classes template < class T > struct traits {}; template <> struct traits<CA> { typedef other_cat cat; }; template <> struct traits<CB> { typedef other_cat cat; }; template <> struct traits<CC> { typedef other_cat cat; }; template <> struct traits<CD> { typedef float_cat cat; }; template <> struct traits<CE> { typedef float_cat cat; }; template < class A, class B, class CAT > void convert( A const &a, B &b, CAT c ); // specialized template-instances for category float_cat ... template < class B > void convert( CA const &a, B &b, float_cat c ) { TEST; } template < class B > void convert( CB const &a, B &b, float_cat c ) { TEST; } template < class B > void convert( CC const &a, B &b, float_cat c ) { TEST; } // ...and other_cat void convert( CA const &a, CB &b, other_cat c ) { TEST; } void convert( CA const &a, CC &b, other_cat c ) { TEST; } /*...*/ } template < class T > void convert( T const& a, T &b ) { b = a; } // call the right function by passing a dummy-instance of type traits::cat template < class A, class B > void convert( A const& a, B &b ) { hidden::convert( a, b, typename hidden::traits<B>::cat() ); }man verzeihe mein englisch...
... und dass ich tatsächlich 5 klassen deklariert hab...
-
I don't have much time to think about this right now, but you could have a look at GIL:
http://www.boost.org/doc/libs/1_36_0/libs/gil/doc/index.htmlMaybe you could use it directly, or maybe you find some clever idea in there that you could borrow

-
hustbaer schrieb:
I don't have much time to think about this right now, but you could have a look at GIL:
http://www.boost.org/doc/libs/1_36_0/libs/gil/doc/index.htmlMaybe you could use it directly, or maybe you find some clever idea in there that you could borrow

Very interesting! In fact, I'm not familiar with Boost, Iterators and other such things. So, I need something simplier. But I will look at color conversion there...
-
Do you have one instance of your class per pixel? I think this will be much overhead.
-
asdfgfvsd schrieb:
Do you have one instance of your class per pixel? I think this will be much overhead.
C++ classes do not have any overhead in particular compared to C-structs or plain old data types. They are more or less syntactic sugar, if you do not use (dynamic) polymorphism. The compiler takes care of optimizations. I think this is called the "zero cost" principle of C++.
-
I think normally int or float arrays are used to represent the pixels of an image. I have never seen an array of classes to store pixles.
Will this have the same speed?std::vector<int> pixels; ... for(...) pixels[pos+0] = r; pixels[pos+1] = g; pixels[pos+2] = b; std::vector<PixelClass> pixels; ... for(...) pixels[pos].r = r; pixels[pos].g = g; pixels[pos].b = b;
-
Will this have the same speed?
Nothing is ever the same.
But both solutions should be close.It all depends on what the compiler will optimize better, and of course on what alignment the compiler will be using for the RGB-struct. And probably a dozen other little things.
-
wandrer schrieb:
example:
[...]
man verzeihe mein englisch...
... und dass ich tatsächlich 5 klassen deklariert hab...Thank you! This is exactly what I need.
In fact, I already have traits of 3 types in my libriary:
enum idColorComponentFamily { ccfUnsignedInteger, ccfSignedInteger, ccfFloatingPoint };I just could't figure out how to use that until I have seen your example.
Now I will do that way:
ccfUnsignedInteger— depend of type,ccfSignedInteger— forbidden, because signed integers are special cases,ccfFloatingPoint— use the same algorithm for bothfloatanddouble.
-
asdfgfvsd schrieb:
I think normally int or float arrays are used to represent the pixels of an image. I have never seen an array of classes to store pixles.
Will this have the same speed?std::vector<int> pixels; ... for(...) pixels[pos+0] = r; pixels[pos+1] = g; pixels[pos+2] = b; std::vector<PixelClass> pixels; ... for(...) pixels[pos].r = r; pixels[pos].g = g; pixels[pos].b = b;Yes, this will have the same speed for most cases.