Home > Net >  How can I combine a templated function with a non-templated function in C ?
How can I combine a templated function with a non-templated function in C ?

Time:10-28

I'm not sure if it's possible but take this snippet of code:

#include <stdio.h>

struct car {
    float speed;
};

struct bike {
    float speed;
};

void drive(float *speed)
{
    printf("driving at speed %f\n", *speed);
}

template<typename T>
void drive(T driveable)
{
    drive(&driveable->speed);
}

int main() {
    struct car car = { .speed = 10.f };
    struct bike bike = { .speed = 20.f };
    drive(&car);
    drive(&bike);
}

The idea is that any object that has a member field of type float and that is named speed can be used transparently with the function drive, thanks to the templated overload of drive which passes the member of the template type to the actual drive function.

Is it possible to combine the templated function with the non-templated function in a single one? Something along the lines of:

template<typename T>
void drive(T driveable)
void drive(float *speed = &driveable->speed)
{
    printf("driving at speed %f\n", *speed);
}

Additionally, would there be another, more elegant way to do what I want?

CodePudding user response:

There is no syntax similar to the one you are proposing. Having two seperate overloads as two seperate overloads is "elegant" enough. Your code is fine as is.

Though, if you want, there is a way to have a single function template that can cover both cases:

#include <cstdio>
#include <type_traits>

struct car {
    float speed;
};

struct bike {
    float speed;
};

template<typename T>
void drive(const T& t)
{
    if constexpr (std::is_same_v<float,T>) {
        printf("driving at speed %f\n",t);
    } else {    
        drive(t.speed);
    }
}

int main() {
    struct car car = { .speed = 10.f };
    struct bike bike = { .speed = 20.f };
    drive(car);
    drive(bike);
    drive(0.5f);
}

Live Demo

Whether this or your code is more elegant is largely a matter of opinions.

PS: There is quite a bit of pointless use of pointers in your code. I removed that. Also in C you should use the C header called cstdio rather than the C header stdio.h.

CodePudding user response:

Is it possible to combine the templated function with the non-templated function in a single one?

Yes, it is possible. On the other hand, having two overloads in your case is more readable.

If you insist to do so, using concepts (since ) and compile time branching using if constexpr (since ) you may do:

#include <concepts> // std::floating_point

// concept for checking whether T has "speed" member which is floating point!
template<typename T>
concept has_float_member = requires (T t) {  // nested requirements
    requires std::floating_point<decltype(t.speed)>;
};

void drive(auto const& driveable)
{
    if constexpr (has_float_member<decltype(driveable)>)
        std::cout << driveable.speed << "\n";
    else
        std::cout << "driving at speed: " << driveable;
}

See a demo in godbolt.org

Now you can call the same function for car, bike, and floats as follows.

drive(car);  // pass car and bike like
drive(bike);
drive(2.f);

If you required to pass the references to the instances, adjust the function accordingly.

  • Related