Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a default function parameter with templates

I am trying to make a method where the user can provide an analytic gradient function if available, but if not it defaults to using a numeric gradient.

To that effect I tried the following:

template<typename V, typename S, S (*Fun)(const V&)> V NumericGradient(const V& p)
{
    const S e = 1.0e-6;
    return {
        Fun(p + V(e, 0, 0)) - Fun(p - V(e, 0, 0)) / S(2) * e,
        Fun(p + V(0, e, 0)) - Fun(p - V(0, e, 0)) / S(2) * e,
        Fun(p + V(0, 0, e)) - Fun(p - V(0, 0, e)) / S(2) * e,
    };
}

template<typename V, typename S = decltype(V()[0])>
std::vector<V> DualContouring(
    S (*DistanceFunction)(const V&),
    V (*GradientFunction)(const V&) = NumericGradient<V, S, decltype(DistanceFunction)>,
    const S span = S(50),
    const unsigned int resolution = 100)
{
    return {};
}

The compiler however is failing with this message (after instantiating it):

terrain_generation.cpp
../Src/examples/TerrainGeneration/terrain_generation.cpp: In function ‘std::vector<_RealType> DualContouring(S (*)(const V&), V (*)(const V&), S, unsigned int) [with V = Eigen::Matrix<float, 3, 1>; S = float]’:
../Src/examples/TerrainGeneration/terrain_generation.cpp:362:63: error: no matches converting function ‘NumericGradient’ to type ‘class Eigen::Matrix<float, 3, 1> (*)(const class Eigen::Matrix<float, 3, 1>&)’
  362 |         [](const Eigen::Vector3f& v) { return v.dot(v) - 10; });
      |                                                               ^
In file included from ../Src/Engine/Renderer/Renderer.hpp:24,
                 from ../Src/examples/TerrainGeneration/terrain_generation.cpp:32:
../Src/Engine/Geometry/DualContouring.hpp:97:56: note: candidate is: ‘template<class V, class S, S (* Fun)(const V&)> V NumericGradient(const V&)’
   97 | template<typename V, typename S, S (*Fun)(const V&)> V NumericGradient(const V& p)
      |                                                        ^~~~~~~~~~~~~~~
../Src/examples/TerrainGeneration/terrain_generation.cpp:362:63: note:   when instantiating default argument for call to ‘std::vector<_RealType> DualContouring(S (*)(const V&), V (*)(const V&), S, unsigned int) [with V = Eigen::Matrix<float, 3, 1>; S = float]’
  362 |         [](const Eigen::Vector3f& v) { return v.dot(v) - 10; });
      |                                                               ^
../Src/examples/TerrainGeneration/terrain_generation.cpp: In function ‘void GenerateTerrain(Gallery&)’:
../Src/examples/TerrainGeneration/terrain_generation.cpp:362:63: error: no matches converting function ‘NumericGradient’ to type ‘class Eigen::Matrix<float, 3, 1> (*)(const class Eigen::Matrix<float, 3, 1>&)’
In file included from ../Src/Engine/Renderer/Renderer.hpp:24,
                 from ../Src/examples/TerrainGeneration/terrain_generation.cpp:32:
../Src/Engine/Geometry/DualContouring.hpp:97:56: note: candidate is: ‘template<class V, class S, S (* Fun)(const V&)> V NumericGradient(const V&)’
   97 | template<typename V, typename S, S (*Fun)(const V&)> V NumericGradient(const V& p)
      |                                                        ^~~~~~~~~~~~~~~
make[1]: *** [TerrainGeneration.make:156: obj/Debug/TerrainGeneration/terrain_generation.o] Error 1
make: *** [Makefile:39: TerrainGeneration] Error 2
make: Leaving directory '/home/makogan/vkengine/Generated'
Traceback (most recent call last):
  File "build.py", line 139, in <module>
    main()
  File "build.py", line 106, in main
    raise Exception("Compilation failed.")
Exception: Compilation failed.

I am not sure what's wrong with the type definitions there. The instantiation of NumericGradient should be type compatible with the expected function.

This is how I am trying to call the stub:

auto contour = DualContouring<Eigen::Vector3f, float>(
        [](const Eigen::Vector3f& v) { return v.dot(v) - 10; });

I am compiling with g++ 9.3 under linux.

Alternative problem

Ok I tried dropping the default arguments alltoghether and force the user to always pass the function pointer.

Somehow even this:

    float (*Distance)(const Eigen::Vector3f&) = [](const Eigen::Vector3f& v) {
        return v.dot(v) - 9;
    };
    auto contour = DualContouring<Eigen::Vector3f, float>(
        Distance, NumericGradient<Eigen::Vector3f, float>, Distance);

Fails to compile with error:

error: no matches converting function ‘NumericGradient’ to type ‘class Eigen::Matrix<float, 3, 1> (*)(const class Eigen::Matrix<float, 3, 1>&)’
  365 |         Distance, NumericGradient<Eigen::Vector3f, float>, Distance);```

Why? The template declaration should be fine.
like image 527
Makogan Avatar asked May 09 '26 17:05

Makogan


1 Answers

Following Jarod42 explanation, NumericGradient's third template argument is a non-type argument and decltype(DistanceFunction) is a type. So, I believe what you wanted to do was to use DistanceFunction, but, as he mentioned, the argument name cannot be used in this context.

As an alternative, you could "demote" the function pointer template argument to a regular function pointer argument:

template<typename V, typename S> V NumericGradient(const V& p, S (*Fun)(const V&));

and pass it around.

Here's a working example: https://godbolt.org/z/qhY938

As another alternative, you could pass DistanceFunction as a template argument to DualContouring.

Here's another working example: https://godbolt.org/z/EnaMjK

like image 95
Pedro Boechat Avatar answered May 11 '26 07:05

Pedro Boechat



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!