2

This has probably been asked in some way before, but I cannot find the right search keywords.

While writing testing functions, I decided to refactor the testing code into a template function:

#include <iostream>
#include <functional>
#include <utility>
#include <vector>

template <typename In, typename Exp >
void runTest (
    std::pair<In, Exp> testParams, 
    Exp (*testFunction)(In)
    /*std::function< Exp(In)> testFunction */ )
{
    Exp result = testFunction(testParams.first);
    std::cout   << "Result : " << (result == testParams.second? "SUCCESS":"FAILURE")
                << " expected : " << testParams.second 
                << " got : "  << result
                << std::endl;
}

Fill a vector with input and expected results and pass the pairs along with the function we want to test. Worked great for one function:

long f1 (long a1)
{
    return a1 + 100;
}

void testf1()
{
    std::vector<std::pair<long, long> > testCases = {
        {100,200},
        {300,400}
    };
    for (auto test : testCases) {
        runTest (test, f1);
    }
}

but then had to test one that takes two parameters. "Ok, no problem, I'll std::bind1st... oh, that's deprecated... std::bind should to it though, right? the first argument and pass that along to runTest"...

long f2 (long a1, long a2) 
{
    return a1+a2;
}

void testf2() 
{
    long a1 = 1234;
    std::vector<std::pair<long, long> > testCases = {
        {0,1234},
        {2,1238},
        {11,1245}
    };
    for (auto test : testCases){
        auto f2bound = std::bind(f2, a1, std::placeholders::_2);
        runTest (test, f2bound);
    }
}

But the compiler says 'no' :

~/src/cpplay/onestens$ g++ -m64 --std=c++11 -o soq.out soQuestionBind.cpp -g
soQuestionBind.cpp: In function ‘void testf2()’:
soQuestionBind.cpp:50:31: error: no matching function for call to ‘runTest(std::pair<long int, long int>&, std::_Bind<long int (*(long int, std::_Placeholder<2>))(long int, long int)>&)’
         runTest (test, f2bound);
                               ^
soQuestionBind.cpp:7:6: note: candidate: template<class In, class Exp> void runTest(std::pair<_T1, _T2>, Exp (*)(In))
 void runTest (
      ^
soQuestionBind.cpp:7:6: note:   template argument deduction/substitution failed:
soQuestionBind.cpp:50:31: note:   mismatched types ‘Exp (*)(In)’ and ‘std::_Bind<long int (*(long int, std::_Placeholder<2>))(long int, long int)>’
         runTest (test, f2bound);
                               ^

I'm a bit behind C++11 (and 14 and 17) but this should be possible, right?

I guess the object returned by std::bind couldn't be coerced into a simple function pointer... so how must my template parameter be defined to accept the bound function?

2
  • 3
    Why not simply using a lambda function? std::bind is kinda obsolete since we have these. Commented Aug 21, 2018 at 13:32
  • 1
    @πάνταῥεῖ That would mean that a1 needs to be captured and then no function pointer :( Commented Aug 21, 2018 at 13:33

1 Answer 1

2

I guess the object returned by std::bind couldn't be coerced into a simple function pointer.

Correct.

The typical way of getting generic callables is to use a simple template parameter. No need to use function pointers, std::function or whatever:

template <typename T, typename U, typename F>
void runTest (
    std::pair<T, U> testParams, 
    F testFunction )
{
    auto result = testFunction(testParams.first);
    // do something ...
}

Now you can use std::bind, but I'd recommend to use a lambda instead.

Sign up to request clarification or add additional context in comments.

4 Comments

Thanks. Simple template parameter with lambda does work, but std::bind still doesn't. It is a little annoying that 'use a lambda' is the knee-jerk response to any question involving bind. std::bind has an understood and documented meaning, whereas an inline lambda could be doing anything.
On the topic of lambdas, how would their use impact template specialization? Are lambdas "typed" like a normal function, defined by parameters and return type? or do their captures also play a role in typing? I wonder how many specializations the compiler is generating for runTest.
@veefu Oh, right. You're supposed to use __1, not __2. Lambdas are classes with a overloaded call operator. runTest once for each type.
_1 did it. Thanks again.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.