Templatising a function applied to a template class

P

Philip Pemberton

Hi,
I'm doing some work with the CImg image processing library, and have this
function that applies to a given image:

unsigned char** MapArray(CImg<unsigned char> &image, int channel){
unsigned char **map;
map = new unsigned char*[image.dimy()];

for( int y=0 ; y<image.dimy() ; y++ )
{
map[y] = &image(0,y,0,channel);
}
return map;
}

Briefly, this is necessary because accessing the image directly is
inefficient -- for every scanline, the Y offset in the pixel array is
calculated total_x_pixels times. This is, of course, rather inefficient.
The Y offset should be calculated once per scanline, then the X offset
should be added to it. This is what the function above does.

CImg is a template class -- effectively, you can use any type for your
pixels as long as that type has a few basic operators available (<, >, +,
etc.). A CImg is declared as (eg.) "CImg<unsigned char> image;".

What I'd like to do is make this function less specialised -- turn it
into a template that I can apply to any CImg object based on an
arithmetic type (float, double, int, char, ...)

Now, I've come up with this:
template<class T, class X> X** MapArray(T &image, int channel)
{
X **map = new X*[image.height()];

for( int y=0 ; y<image.height() ; y++ )
{
map[y] = &image(0,y,0,channel);
}
return map;
}

which I can call like this:
basefloat **rmap = MapArray<CImg<basefloat>, basefloat>(image,
C_RED);

What I'd rather have is something like:
basefloat **rmap = MapArray<CImg<basefloat>>(image, C_RED);

Which eliminates the second instance of basefloat, saving me typing and
the potential for a ****-up when refactoring code to use a different type
for image pixels.

The question is, does anyone know how I can do what I want to do?
Alternatively, is there a cleaner/tidier/"better" way to do this?

Thanks,
Phil.
 
V

Victor Bazarov

Philip said:
Hi,
I'm doing some work with the CImg image processing library, and have this
function that applies to a given image:

unsigned char** MapArray(CImg<unsigned char> &image, int channel){
unsigned char **map;
map = new unsigned char*[image.dimy()];

for( int y=0 ; y<image.dimy() ; y++ )
{
map[y] = &image(0,y,0,channel);
}
return map;
}

Briefly, this is necessary because accessing the image directly is
inefficient -- for every scanline, the Y offset in the pixel array is
calculated total_x_pixels times. This is, of course, rather inefficient.
The Y offset should be calculated once per scanline, then the X offset
should be added to it. This is what the function above does.

CImg is a template class -- effectively, you can use any type for your
pixels as long as that type has a few basic operators available (<, >, +,
etc.). A CImg is declared as (eg.) "CImg<unsigned char> image;".

What I'd like to do is make this function less specialised -- turn it
into a template that I can apply to any CImg object based on an
arithmetic type (float, double, int, char, ...)

Now, I've come up with this:
template<class T, class X> X** MapArray(T &image, int channel)
{
X **map = new X*[image.height()];

for( int y=0 ; y<image.height() ; y++ )
{
map[y] = &image(0,y,0,channel);
}
return map;
}

which I can call like this:
basefloat **rmap = MapArray<CImg<basefloat>, basefloat>(image,
C_RED);

What I'd rather have is something like:
basefloat **rmap = MapArray<CImg<basefloat>>(image, C_RED);

Which eliminates the second instance of basefloat, saving me typing and
the potential for a ****-up when refactoring code to use a different type
for image pixels.

The question is, does anyone know how I can do what I want to do?
Alternatively, is there a cleaner/tidier/"better" way to do this?

A "better" way would be to use the internal definition from CImg instead
of supplying it again. *If* it has it, that is. Something like

template<class Img>
typename Img::value_type** MapArray(Img &image, int channel)
{
Img::value_type **map = new Img::value_type*[ ...

It relies on the presence of 'value_type' typedef that should resolve to
the type with which you instantiate CImg template. Again, I don't know
whether it's there or not and what's it called. Look it up.

If that typedef doesn't exist, you could try to always assume CImg is
used and do something like


template<class Val>Val** MapArray(CImg<Val> &image, int channel)
{
Val **map = new Val*[ ...

which you'd call either MapArray(image, C_RED) or, if deducing of the
template argument fails, MapArray<float>(image, C_RED);

V
 
P

Philip Pemberton

A "better" way would be to use the internal definition from CImg instead
of supplying it again. *If* it has it, that is. Something like

It does -- as "value_type". I wondered why that was there...
template<class Img>
typename Img::value_type** MapArray(Img &image, int channel) {
Img::value_type **map = new Img::value_type*[ ...

That's pretty much what I want -- a way to apply MapArray to any numeric
type without having five or six separate MapArray fns. And it even pulls
the pixel type out of the CImg itself -- nice!

One thing though, that code didn't compile as-was. The declaration was
fine, but g++ kept throwing this error:

35: 'map' was not declared in this scope
(Line 35: Img::value_type **map = new Img::value_type*[ ... )

I did some Google searching, and figured out that it was probably down to
a few missing 'typename' keywords, and rewrote it as:

typename Img::value_type **map = new typename Img::value_type*[ ...

This seems to compile and work fine.

Thanks,
--
Phil.
(e-mail address removed)
http://www.philpem.me.uk/
If mail bounces, replace "09" with the last two digits of the current
year.
 
V

Victor Bazarov

Philip said:
A "better" way would be to use the internal definition from CImg instead
of supplying it again. *If* it has it, that is. Something like

It does -- as "value_type". I wondered why that was there...
template<class Img>
typename Img::value_type** MapArray(Img &image, int channel) {
Img::value_type **map = new Img::value_type*[ ...

That's pretty much what I want -- a way to apply MapArray to any numeric
type without having five or six separate MapArray fns. And it even pulls
the pixel type out of the CImg itself -- nice!

One thing though, that code didn't compile as-was. The declaration was
fine, but g++ kept throwing this error:

35: 'map' was not declared in this scope
(Line 35: Img::value_type **map = new Img::value_type*[ ... )

I did some Google searching, and figured out that it was probably down to
a few missing 'typename' keywords, and rewrote it as:

typename Img::value_type **map = new typename Img::value_type*[ ...

This seems to compile and work fine.

Yes. I remembered to put it in one place, but forgot to do it in the
other. Happens :). Thanks for the correction.

You're most welcome!

V
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top