sjm324 sjm324 - 2 months ago 7
C++ Question

sharing common functionality for template functions when the template changes a small portion

My project has a couple of awkward locations with respect to templates. I understand that my usage is slightly non-standard, but the awkwardness as fundamental benefits (primarily, unrolling a bunch of loops). AKA please do not respond with "oh you shouldn't be using templates". The awkwardness involved has empirically been shown to have over a 10x speedup...

The templates below use

enum class SomeThing : uint8_t
. There is a

template <SomeThing ST>
struct projection_functor {
static constexpr unsigned WIDTH = 640u;/// *** only in
static constexpr unsigned HEIGHT = 480u;/// *** specializations...
void toXYZ(...);
...
};


It is a guarantee that every possible value of
enum class SomeThing
has a specialized version. Furthermore, the un-specialized version does not actually provide
WIDTH
and
HEIGHT
, only the specializations do (this is a compile-time assurance measure on my part).

What I would like to do is define a function using these closures:

template <SomeThing ST>
void forEachXYZ(...params...) {
if(ST == SomeThing::FIRST)
using proj = projection_functor<SomeThing::FIRST>;
else if(ST == SomeThing::SECOND)
using proj = projection_functor<SomeThing::SECOND>;
else
throw std::runtime_error("Unsupported...");

for(unsigned i = 0; i < proj::WIDTH * proj::HEIGHT; ++i) {
// ... setup ...
proj pf;
pf.toXYZ(...);
}
}


This does not compile, though, the problem being
proj
is not officially defined in every case I guess. This is a really ugly but functional solution, that I would like to just keep in the
forEachXYZ
:

#define THIS_UGLY_MACRO() \
for(unsigned int i = 0; n < proj::WIDTH * proj::HEIGHT; ++i) { \
/* ... setup ... */ \
proj pf; \
pf.toXYZ(...); \
}


And now I can call

template <SomeThing ST>
void forEachXYZ(...params...) {
if(ST == SomeThing::FIRST) {
using proj = projection_functor<SomeThing::FIRST>;
THIS_UGLY_MACRO();
}
else if(ST == SomeThing::SECOND) {
using proj = projection_functor<SomeThing::SECOND>;
THIS_UGLY_MACRO();
}
else
throw std::runtime_error("Unsupported...");
}


Which apparently works because it is scoped to the
if
statements that are evaluated from the template parameter comparison.

Is this the only solution, or is there a better way to be able to inform the loop of what
proj
actually is, dependent upon the template parameter?

I suppose writing a helper function could be done, but this is more a higher level "what can I actually do with templates in this fashion" question.

Thank you for any advice :)

Answer

You can define a template instead of a macro:

template <class Projection>
void projectXYZ(...params...) {
    for(unsigned int i = 0; n < proj::WIDTH * proj::HEIGHT; ++i) {
        /* ... setup ... */
        Projection pf;
        pf.toXYZ(...);
    }
}

And then call it:

template <SomeThing ST>
void forEachXYZ(...params...) {
    if(ST == SomeThing::FIRST) {
        projectXYZ<projection_functor<SomeThing::FIRST>>(std::move(param), ...);
    }
    else if(ST == SomeThing::SECOND) {
        projectXYZ<projection_functor<SomeThing::SECOND>>(std::move(param), ...);
    }
    else {
        throw std::runtime_error("Unsupported...");
    }
}