Home > Back-end >  How to add void/null as default argument to a function/lambda pointer, in C ?
How to add void/null as default argument to a function/lambda pointer, in C ?

Time:10-22

Present signature is

template<class TypeData,typename TypeFunc>
bool isPrime(const TypeData& n,TypeFunc fSqrt,bool debug = false)

and this works perfectly with

std::cout<<(isPrime(n,fSqrt)?"Positive":"Negative")<<'\n';

But, my intension is something like

template<class TypeData,typename TypeFunc>
bool isPrime(const TypeData& n,TypeFunc fSqrt = nullptr,bool debug = false)

or

template<class TypeData,typename TypeFunc>
bool isPrime(const TypeData& n,TypeFunc fSqrt = NULL,bool debug = false)

to be called by

std::cout<<(isPrime(n)?"Positive":"Negative")<<'\n';

Overloading is not possible due to a static variable inside the function.
Only different class TypeData should give different template-functions for this function-template.

Please help me out with the proper syntax.
In case C does not support this, kindly advice me with alternative approaches.

Thank you.


Compile Errors

for TypeFunc fSqrt = nullptr

main.cpp:90:23: error: no matching function for call to ‘isPrime(int&)’
  std::cout<<(isPrime(n)?"Positive":"Negative")<<'\n';
                       ^
main.cpp:9:49: note: candidate: template bool isPrime(const TypeDate&, TypeFunc, bool)
 template<class TypeDate,typename TypeFunc> bool isPrime(const TypeDate& n,TypeFunc fSqrt = nullptr,bool debug = false) {
                                                 ^~~~~~~
main.cpp:9:49: note:   template argument deduction/substitution failed:
main.cpp:90:23: note:   couldn't deduce template parameter ‘TypeFunc’
  std::cout<<(isPrime(n)?"Positive":"Negative")<<'\n';
                       ^

for TypeFunc fSqrt = NULL

main.cpp:90:23: error: no matching function for call to ‘isPrime(int&)’
  std::cout<<(isPrime(n)?"Positive":"Negative")<<'\n';
                       ^
main.cpp:9:49: note: candidate: template bool isPrime(const TypeDate&, TypeFunc, bool)
 template<class TypeDate,typename TypeFunc> bool isPrime(const TypeDate& n,TypeFunc fSqrt = NULL,bool debug = false) {
                                                 ^~~~~~~
main.cpp:9:49: note:   template argument deduction/substitution failed:
main.cpp:90:23: note:   couldn't deduce template parameter ‘TypeFunc’
  std::cout<<(isPrime(n)?"Positive":"Negative")<<'\n';
                       ^

They are basically the same.

CodePudding user response:

You can use a default initialized std::function with the correct prototype for that:

template<class TypeData>
bool isPrime(const TypeData& n,std::function<const decltype(n)&(const decltype(n)&)> fSqrt={},bool debug = false)

You can then check in the function if it is valid or the default one by simply if (fSqrt)

full example: https://godbolt.org/z/zfMazebso

The prototype must be dependent only on the datatype of n, otherwise there is no default parameter possible, e.g., the compiler cannot deduce a type out of nothing.

CodePudding user response:

You can use std::identity as the default TypeFunc type.

#include <functional>

template<class TypeData,typename TypeFunc = std::identity>
bool isPrime(const TypeData& n,TypeFunc fSqrt = {}, bool debug = false) {
  if constexpr (std::is_same_v<TypeFunc, std::identity>) return false;
  else {
    // ...
  }
}

Demo.

CodePudding user response:

Overloading actually is an option, you can let one overload call the other one:

template<class TypeData, typename TypeFunc>
bool isPrime(const TypeData& n, TypeFunc fSqrt, bool debug = false);

template<class TypeData>
bool isPrime(const TypeData& n, bool debug = false)
{
    using std::sqrt;
    if constexpr (std::is_integral_v<TypeData>)
    {
        return isPrime(n, static_cast<double(*)(double)>(sqrt), debug);
    }
    else if constexpr (std::is_floating_point_v<TypeData>)
    {
        return isPrime(n, static_cast<TypeData(*)(TypeData)>(sqrt), debug);
    }
    else
    {
        // this covers e.g. std::complex
        return isPrime(n, static_cast<TypeData(*)(TypeData const&)>(sqrt), debug);
        // for any other type we assume the overload accepts by
        // const reference as well (if there's one at all...)
        // if not, we still can fall back to the other overload
    }
}

This solution selects an appropriate square root function right away, something you would have had to solve anyway if the function argument had defaulted to nullptr.

  • Related