0

I'm trying to make a function template in a header file that accepts a generic function pointer and packed arguments. The function template would invoke the received function pointer using the packed arguments. My goal is to calculate and return the execution time of the function pointer.

#ifndef LOGTIME_HPP
#define LOGTIME_HPP

#include <chrono>
#include <ratio>

template<typename Function, typename... Args>
double getExecutionTime(Function&& function, Args&&... args) {
    auto t1 = std::chrono::high_resolution_clock::now();
    std::invoke(std::forward<Function>(function), std::forward<Args>(args)...);
    auto t2 = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double, std::milli> ms = t2 - t1;
    return ms.count();
}

#endif

This seems to only work for function pointers that aren't member functions of a class or struct. This is some example code using the function template:

#include <iostream>
#include <thread>
#include <random>

#include "LogTime.hpp" // header including getExecutionTime()

class Obj
{
public:
    int testFunc(int dur, int num) {
        std::cout << "test" << num;
        for (int i = 0; i < dur; i++) {
            std::cout << ".";
            std::this_thread::sleep_for(std::chrono::milliseconds(1));
        }
        return 2;
    }
};

int testFunc(int dur, int num) {
    std::cout << "test" << num;
    for (int i = 0; i < dur; i++) {
        std::cout << ".";
        std::this_thread::sleep_for(std::chrono::milliseconds(1));
    }
    return 1;
}

int main()
{
    std::random_device dev;
    std::mt19937 rng(dev());
    std::uniform_int_distribution<> uniform_dist(1, 100);

    Obj obj = Obj();

    for (int i = 0; i < 10; i++) {
        int rand = uniform_dist(rng);
        std::cout << "elapsed: "
            // this works
            << getExecutionTime(testFunc, uniform_dist(rng), i) << std::endl;
            
            // this doesn't work 
            << getExecutionTime(Obj::testFunc, uniform_dist(rng), i) << std::endl;
    }
}

My issue is that Obj::testFunc is failing. I know that if Obj::testFunc was static, then the function would execute fine. I've read that std::invoke can invoke a member function by passing an instance of the class type. However, I don't know how to incorporate this into the function template (I've never used templates before now).

I would appreciate any help or insight.

6
  • 1
    The member function has no object on which to be called upon. Try using std::bind(Obj::testFunc, obj). Commented Oct 22, 2021 at 21:47
  • I would expect that your C++ textbook will have several examples of doing something like this, this is a fairly common topic that's covered in every textbook. Is there something in your textbook's description of this task that's unclear to you? It will be more helpful if you can explain what's so confusing about your textbook's explanation of pointers to class methods. Commented Oct 22, 2021 at 21:50
  • @SamVarshavchik I don't have a textbook; I was just using cppreference as a guide. This is my first time using templates so it's a bit confusing. I understand that I need to pass a reference of the class type in the template, but don't know how to. Commented Oct 22, 2021 at 22:23
  • You should seriously consider getting a good C++ textbook, this is the most complicated and the hardest to learn general purpose programming language in use today. I use cppreference.com all the time, but it's not a replacement for a good textbook. If you are already an experienced C++ developer this is a good resource to look up some nuanced aspects, or new language features. Commented Oct 22, 2021 at 22:54
  • @SamVarshavchik thanks for the resource. At my job we're using C++ just like C, and stuff like templates and parameter packs are banned for us to use lol. I'm working on personal projects and want to use these cool C++ features, so these help a lot. Commented Oct 22, 2021 at 22:58

1 Answer 1

1

Non-static member functions have an implicit parameter of the class type as the first parameter of the function, and it is that object which is mapped to the this pointer. That means that you need to pass an object of the class type as the first argument after the member function pointer like

<< getExecutionTime(&Obj::testFunc, obj, uniform_dist(rng), i) << std::endl;

or you can use a lambda instead like

<< getExecutionTime([&](){ obj.testFunc(uniform_dist(rng), i); }) << std::endl;
Sign up to request clarification or add additional context in comments.

3 Comments

How would this change my function template? Can I please get an example?
@pom_jam You shouldn't need to change your template. It should work with either of the calls show here.
Oh I see. How is the template function mapping the obj instance to &Obj::testFunc? Is there any documentation on this feature?

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.