Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Template Class: template paramaters given at runtime, how to avoid repetitve large switch cases?

I'm currently working on an image processing application, mainly based on C++ and ITK.

1. The Situation

I have node classes (e.g. FlipFilter) derived from the same base class. A node gets a struct with an image_ptr and all of the meta-information (dimension of the Image, PixelType (e.g. RGB, RGBA, scalar) and ComponentType (e.g. Float, int). The node has to instantiate an ITK-Filter, based on these Meta-Informations, which can change on every input-image.

2. The Problem

These ITK-Filters need the ImageType (e.g. RGB with ComponentType UCHAR) as a template argument. A template class has to be instantiated at compile-time. My node is getting the image with its type at runtime. So I somehow have to create all permutations of the filter for each node and then use the appropriate instantiation.

3. My current Solution

This struct contains all the meta-information + a smart-pointer pointing towards the actual image. I'm using a base pointer of the image because the image itself is also a template (later I'm downcasting).

struct ImageData
{
    short NumberOfDimensions;
    itk::ImageIOBase::IOComponentType ComponentType;
    itk::ImageIOBase::IOPixelType PixelType;
    itk::DataObject::Pointer Image;
    ImageData() {}
    ~ImageData() {}
};

This is the update function of my node. It is supposed to create the filter an execute it on an image.

void LitkFlipImageFilter::update()
{
    if (Input1 == nullptr)
        throw(std::runtime_error("Input1 not set"));


    Input1->update();

    ImageData Input1Data = Input1->getOutput();

    switch (Input1Data.PixelType)
    {
        default:
        {
            throw std::runtime_error("Type not Supported");
            break;
        }
        case itk::ImageIOBase::RGB:
        {
            switch (Input1Data.ComponentType)
            {
                default:
                {
                    throw std::runtime_error("Type not Supported");
                    break;
                }
                case itk::ImageIOBase::IOComponentType::UCHAR:
                {
                    using PixelType = itk::RGBPixel< unsigned char >;
                    using ImageType = itk::Image < PixelType, 2 >;
                    itk::FlipImageFilter<ImageType>::Pointer filter = itk::FlipImageFilter<ImageType>::New();
                    //do stuff

                    break;
                }
            break;
            }
        }
    }
}

4. The Problem with my Solution

It's working but creates a lot of repetitive code and large nested switch cases. Do you know a more elegant way of solving this problem?

Thank you!

like image 762
mdin Avatar asked Dec 05 '25 02:12

mdin


1 Answers

The high level processing you want is:

template <typename PixelType>
void do_stuff()
{
    using ImageType = Image < PixelType, 2 >;
    ...do stuff...
}

You can create one verbose but reusable (by varying the "Fn" code to dispatch to) version of your switching code:

template <typename Fn>
void dispatch(PixelType pt, ComponentTypeId ct, Fn fn) {
    switch (pt)
    {
      case RGB:
        switch (ct) {
          case Uint8_t: fn(RGBPixel<uint8_t>{}); return;
          case Float:   fn(RGBPixel<float>{}); return;
        };
      case RGBA:
        switch (ct) {
          case Uint8_t: fn(RGBAPixel<uint8_t>{}); return;
          case Float:   fn(RGBAPixel<float>{}); return;
        };
      case Scalar:
        switch (ct) {
          case Uint8_t: fn(ScalarPixel<uint8_t>{}); return;
          case Float:   fn(ScalarPixel<float>{}); return;
        };
    }
}

Then, call it like this:

dispatch(runtime_pixel_type, runtime_component_type, 
         [](auto pt) { do_stuff<decltype(pt)>(); });

Notes:

  • using a default-constructed "XXXPixel" argument to the lambda is ugly - C++2a is supposed to introduce proper templated lambdas that might (?!) clean this up.

  • you can chain several "dispatch" functions that each dispatch based on one runtime variable to avoid a multiplicative explosion of switch cases; that scales better but is overkill here, and you'd have to work around PixelType being a template.

  • you can add your default: throws back in - no need to break (or return) after them as they never return to the following line of code

like image 50
Tony Delroy Avatar answered Dec 07 '25 17:12

Tony Delroy



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!