www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - A good advertisement for 'static if'

reply "Craig Dillabaugh" <craig.dillabaugh gmail.com> writes:
I am not sure if this belongs in D.learn, but it might be of 
interest.  I was writing some C++ code for a project at work and 
have a class that stores image data from a file.  The image data 
can be in just about any numeric (int/float/complex) type, so I 
wanted a 'wrapper' class/struct that could hold any type of data 
without being 'parameterized' (we use templates for image access).

My class contains a union ( called 'data') with every possible 
type of pointer and parameter for indicating the data type. 
However, at some point code eventually needs to get at the data, 
so I have the following beauty of a template method, (calling the 
image structure RAWImageDataStore was a bad design decision on my 
part, need to change that soon, its very Java-esque):

template< class T >
RAWImageDataStore<T>* getBandData( )
{
    T t;
	
    //Check struct data type vs template type.
    if( datatype == TYPE_8u && typeid(t) == typeid(uint8_t) ) {
       return reinterpret_cast< RAWImageDataStore<T>* >( data.t8u 
);
    }
    else if ( datatype == TYPE_16s && typeid(t) == typeid(int16_t) 
) {
       return reinterpret_cast< RAWImageDataStore<T>* >( data.t16s 
);
    }
    //a number of types left out, I am sure you get the idea.
    //but you need to see the complex types, they are beautiful.
    else if ( datatype == TYPE_C16s &&
              typeid(t) ==  typeid(std::complex<int16_t>) )
    {
        return reinterpret_cast< RAWImageDataStore<T>* >( 
data.tC16s );
    }	
    \\OK, you only really needed to see one of the complex types 
:o)	
    else if( datatype == TYPE_UNKNOWN ) {
	    std::cerr << "Cannot access band with unknown data type."
                       << std::endl;
	   return 0;
    } //+ a bit more error handling code.

Initially this didn't compile because I was missing the 
"reinterpret_cast" statements.  They effectively do nothing. If 
the template type is int8_t then I return the data.t8u pointer, 
which is a RAWImageDataStore<int8_t>*, but have to cast it to  
RAWImageDataStore<int8_t>*.  I must do this because when I call 
the method type int16_t my "return data.t8u" returns the wrong 
type of pointer for the method, even though I know that if the 
type is int16_t this statement can never be reached.

I know there was some debate in the C++ community about whether 
they should adopt D-like "static if", which would have solved 
this problem, since it would compile the illegal code right out 
of existence.

Maybe there is a better way to do this in C++, but I thought I 
would post here as a case-study in the usefulness of 'static if'.

Craig
Dec 12 2013
parent reply "FreeSlave" <freeslave93 gmail.com> writes:
On Thursday, 12 December 2013 at 14:55:28 UTC, Craig Dillabaugh 
wrote:
 I am not sure if this belongs in D.learn, but it might be of 
 interest.  I was writing some C++ code for a project at work 
 and have a class that stores image data from a file.  The image 
 data can be in just about any numeric (int/float/complex) type, 
 so I wanted a 'wrapper' class/struct that could hold any type 
 of data without being 'parameterized' (we use templates for 
 image access).

 My class contains a union ( called 'data') with every possible 
 type of pointer and parameter for indicating the data type. 
 However, at some point code eventually needs to get at the 
 data, so I have the following beauty of a template method, 
 (calling the image structure RAWImageDataStore was a bad design 
 decision on my part, need to change that soon, its very 
 Java-esque):

 template< class T >
 RAWImageDataStore<T>* getBandData( )
 {
    T t;
 	
    //Check struct data type vs template type.
    if( datatype == TYPE_8u && typeid(t) == typeid(uint8_t) ) {
       return reinterpret_cast< RAWImageDataStore<T>* >( 
 data.t8u );
    }
    else if ( datatype == TYPE_16s && typeid(t) == 
 typeid(int16_t) ) {
       return reinterpret_cast< RAWImageDataStore<T>* >( 
 data.t16s );
    }
    //a number of types left out, I am sure you get the idea.
    //but you need to see the complex types, they are beautiful.
    else if ( datatype == TYPE_C16s &&
              typeid(t) ==  typeid(std::complex<int16_t>) )
    {
        return reinterpret_cast< RAWImageDataStore<T>* >( 
 data.tC16s );
    }	
    \\OK, you only really needed to see one of the complex types 
 :o)	
    else if( datatype == TYPE_UNKNOWN ) {
 	    std::cerr << "Cannot access band with unknown data type."
                       << std::endl;
 	   return 0;
    } //+ a bit more error handling code.

 Initially this didn't compile because I was missing the 
 "reinterpret_cast" statements.  They effectively do nothing. If 
 the template type is int8_t then I return the data.t8u pointer, 
 which is a RAWImageDataStore<int8_t>*, but have to cast it to  
 RAWImageDataStore<int8_t>*.  I must do this because when I call 
 the method type int16_t my "return data.t8u" returns the wrong 
 type of pointer for the method, even though I know that if the 
 type is int16_t this statement can never be reached.

 I know there was some debate in the C++ community about whether 
 they should adopt D-like "static if", which would have solved 
 this problem, since it would compile the illegal code right out 
 of existence.

 Maybe there is a better way to do this in C++, but I thought I 
 would post here as a case-study in the usefulness of 'static 
 if'.

 Craig
In C++ you can use partial specialization to achieve what you want. class Storage { public: union { float* fdata; int* idata; } data; }; template<typename T> T* get(Storage& stor) { return 0; //or throw exception } template<> float* get<float>(Storage& stor) { return stor.data.fdata; } template<> int* get<int>(Storage& stor) { return stor.data.idata; } int main() { Storage stor; float* fdata = get<float>(stor); return 0; }
Dec 12 2013
next sibling parent "FreeSlave" <freeslave93 gmail.com> writes:
With some improvements you also can provide compile-time error 
about instantiation of non-specialized function (i.e. which has T 
as parameter), but I'm not sure what's good way to do it in C++.
Dec 12 2013
prev sibling parent "Craig Dillabaugh" <craig.dillabaugh gmail.com> writes:
On Thursday, 12 December 2013 at 17:34:13 UTC, FreeSlave wrote:
 On Thursday, 12 December 2013 at 14:55:28 UTC, Craig Dillabaugh 
 wrote:
 I am not sure if this belongs in D.learn, but it might be of 
 interest.  I was writing some C++ code for a project at work 
 and have a class that stores image data from a file.  The 
 image data can be in just about any numeric 
 (int/float/complex) type, so I wanted a 'wrapper' class/struct 
 that could hold any type of data without being 'parameterized' 
 (we use templates for image access).
clip
 Maybe there is a better way to do this in C++, but I thought I 
 would post here as a case-study in the usefulness of 'static 
 if'.

 Craig
In C++ you can use partial specialization to achieve what you want. class Storage { public: union { float* fdata; int* idata; } data; }; template<typename T> T* get(Storage& stor) { return 0; //or throw exception } template<> float* get<float>(Storage& stor) { return stor.data.fdata; } template<> int* get<int>(Storage& stor) { return stor.data.idata; } int main() { Storage stor; float* fdata = get<float>(stor); return 0; }
Thanks for this suggestion, it avoids the reinterpret cast nicely. I've used template specialization in other cases, but it didn't occur to me in this case (plus adding the 'reinterpret_cast' required less refactoring on my part at the time). However, I may switch my code to use this method since the resulting executable should be smaller/faster since the big if ... else statement isn't inserted with every template instantiation. However neither solution is as nice as 'static if' in D. The benefits of having all my type checks in one space, and the 'dead' code is eliminated in the instantiations. Cheers, Craig
Dec 12 2013