Home > Software engineering >  How to enable template parameter if optional function parameter is set?
How to enable template parameter if optional function parameter is set?

Time:05-27

I have a parsing function that I want to branch based on whether a length option is set or not. If the length option is set, the function should always check if the decreased length equals to 0. If not, it only checks for null termination. Based on this little detail I don't want to rewrite my whole function, so here's what I came up with:

#include <iostream>

const char* str = "some random string";

template <bool LengthOpt = false>
void parse(const char* ch, size_t len = 0)
{
    while ( 1 ) {
        if constexpr(LengthOpt) {
            if ( len == 0 ) {
                std::cout << std::endl;
                return ;
            } else {
                len--;
            }
        } else {
            if ( !(*ch) ) {
                std::cout << std::endl;
                return ;
            }
        }
        std::cout << *ch;
          ch;

        /* big part starts here */
    }
}

int main()
{
    parse<true>(str, 5);
    parse(str);
}

godbolt

What bugs me is that I always have to specify both, length AND template parameter to go with the length option. So I'm wondering:

  • Is there a way to constexpr branch based on whether an optional parameter is set or not?
  • Could I infer template parameters from whether the optional parameter is set?

Note: This is a contrived example showing just the detail. I've added comments in the code where the actual parsing would happen.

CodePudding user response:

I think you can use function overloading here:

#include <iostream>

const char* str = "some random string";

void parse(const char* ch, size_t len)
{
    while ( 1 ) {
        if ( len == 0 ) {
            std::cout << std::endl;
            return ;
        } else {
                len--;
        }

        std::cout << *ch;
          ch;

        /* the big part can be moved into separate function */
       bigPart(ch);
    }
}

void parse(const char* ch)
{
    while ( 1 ) {
        if ( !(*ch) ) {
            std::cout << std::endl;
            return ;
        }

        std::cout << *ch;
          ch;

        /* the big part can be moved into separate function */
       bigPart(ch);

    }
}

int main()
{
    parse(str, 5);
    parse(str);
}

CodePudding user response:

You can do this with a pair of overloads that delegate to a templated version to avoid duplicating code:

template <bool LengthOpt>
void do_parse(const char* ch, std::size_t len) {
    // ...
}

void parse(const char* ch) {
    do_parse<true>(ch, 0);
}

void parse(const char* ch, std::size_t len) {
    do_parse<false>(ch, len);
}

Or you can switch to an iterator-based approach:

template<typename Iterator, typename Sentinel>
void do_parse(Iterator it, Sentinel end) {
    for (; it != end;   it) {
        char c = *it;
        // ...
    }
}

struct null_sentinel_t {};
inline constexpr null_sentinel_t null_sentinel{};

inline constexpr bool operator==(null_sentinel_t, const char* p) noexcept { return *p == 0; }
// The next 3 overloads are not necessary in C  20
inline constexpr bool operator==(const char* p, null_sentinel_t) noexcept { return *p == 0; }
inline constexpr bool operator!=(null_sentinel_t, const char* p) noexcept { return *p != 0; }
inline constexpr bool operator!=(const char* p, null_sentinel_t) noexcept { return *p != 0; }

void parse(const char* ch) {
    do_parse(ch, null_sentinel);
}
void parse(const char* ch, std::size_t len) {
    do_parse(ch, ch   len);
}
  • Related