Search code examples
c++c++-concepts

C++ pass function as template argument within constructor


I'd like to replace a std::function with a template parameter, since everything is known at compile time. No I know there a many answers here in the forum and over the internet how to pass and directly consume a templated function (for ex here https://rules.sonarsource.com/cpp/RSPEC-5213/?search=std%3A%3Afunction)

However in my case: I am using the templated function in a private member. Therefore I have to set up the templated function within the constructor.

Now I have 2 questions:

  1. how can I pass and store the templated function as a member parameter for later use in a private function

  2. is it possible to use concepts to enforce that all functions passed as a template argument are using a specific signature (in my case std::string(const uint32_t u32ReadOffset, const uint32_t u32MaxReadLength))

So far i have come up with this

#include <string_view>
#include <concepts>
#include <string>
#include <cstdint>

template <typename T, typename U, typename V>
concept IDmmy = requires(T a, const std::string_view b)
{
  { a.template foo<U>() };
  { a.template bar<V>(b) };
};


template<typename FnGetFunction_T>
class Foo
{
    public:
    explicit Foo(FnGetFunction_T fn);
    
    void publicFunction();
    
    private:
    
    FnGetFunction_T m_fn;
    
    void privateFunction();
};


template<typename FnGetFunction_T>
Foo<FnGetFunction_T>::Foo(FnGetFunction_T fn):
m_fn(fn)
{
    
}

template<typename FnGetFunction_T>
void Foo<FnGetFunction_T>::publicFunction()
{
    //do some checking first
    //call private function
    privateFunction();
}

template<typename FnGetFunction_T>
void Foo<FnGetFunction_T>::privateFunction()
{
    //collect some internal data
    std::string strRes = m_fn(0,0);
    //do something with strRes
}

static std::string DummyRead(const uint32_t u32Offset, uint32_t u32MaxReadLength)
{
    if(0 != u32Offset && 0 != u32MaxReadLength)
    {
        //avoid compiler warning in this mock
    }
    return "";
}


int main()
{
    Foo<decltype(DummyRead)> foo = Foo<decltype(DummyRead)>(DummyRead);
    return 0;
}

https://coliru.stacked-crooked.com/

Unfortunately this gives me a compiler error invalidly declared function type. Could you help me how to fix this?

Also is it possible to enforce which concepts that FnGetFunction_T always looks like this std::string(const uint32_t u32Offset, uint32_t u32MaxReadLength) ?

thx once again guys for your help :)


Solution

  • Functions are not like other objects. When you pass a function as arugment you pass a function pointer not the function itself.

    If you fix that:

    Foo<decltype(&DummyRead)> foo = Foo<decltype(&DummyRead)>(DummyRead);
    

    The code compiles: https://godbolt.org/z/Tv1ceqYzs

    However, with Class Template Argument Deduction (CTAD) and making use of function to pointer to function decay it simply becomes:

    Foo foo = Foo(DummyRead);
    

    Live Demo


    Also is it possible to enforce which concepts that FnGetFunction_T always looks like this std::string(const uint32_t u32Offset, uint32_t u32MaxReadLength) ?

    If that is what you want then there is no point in making the type of the function a template argument in the first place. You can use usign function_type = std::string(uint32_t,uint32_t) as the type of the function.