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.

pom_jam
  • 115
  • 6
  • 1
    The member function has no object on which to be called upon. Try using `std::bind(Obj::testFunc, obj)`. – ichramm Oct 22 '21 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. – Sam Varshavchik Oct 22 '21 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. – pom_jam Oct 22 '21 at 22:23
  • You should seriously consider getting [a good C++ textbook](https://stackoverflow.com/questions/388242/), 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. – Sam Varshavchik Oct 22 '21 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. – pom_jam Oct 22 '21 at 22:58
  • C++ without templates and parameters is not C++. LOL, indeed. This is the right approach -- stay up to date on your skills, but I strongly suggest some kind of a more primer-oriented resource, like a textbook. – Sam Varshavchik Oct 22 '21 at 23:02

1 Answers1

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;
NathanOliver
  • 171,901
  • 28
  • 288
  • 402