转换成员函数的指针

时间:2019-03-28 15:27:55

标签: c++

我正在写一个课程来进行时序分析;它会“记住”代码中各个点的时间,并根据要求转储其结果。

有效。

但是我想让它通过调用现有的记录器函数来转储结果。我已经接近了,但是无法编译。这是一个简短的版本:

class TheLogger
{
public:
    TheLogger() {} 
    void log(const char* format, ...)
    {
        va_list argptr;
        va_start(argptr, format);
        vfprintf(stderr, format, argptr);
        va_end(argptr);
    }
};

// And there's a different unrelated logger with same signature
class AnotherLogger
{
public:
    TheLogger() {} 
    void logMessage(const char* format, ...)
    {
        va_list argptr;
        va_start(argptr, format);
        vsprintf(buf, format, argptr);
        va_end(argptr);
        doSomething(buf);
    }
};

class TqTimingPoint
{
public:
    TqTimingPoint(const std::string &name, void (*logger)(const char* format, ...) ) :
        mName(name),
        mpLoggerFcn(logger)
    { }

    void dump()
    {
        (this->mpLoggerFcn)( mName.c_str() );
    }

private:
    std::string               mName;   // Name of this collector

    void (*mpLoggerFcn)(const char* format, ...);
};

int
main(int, char **)
{
    TheLogger *pLogger = new TheLogger;
    pLogger->log("yo baby\n");

    TqTimingPoint tp1("testCom", &TheLogger::log);

    AnotherLogger *pLogger2 = new AnotherLogger;
    TqTimingPoint tp2("testCom", &AnotherLogger::logMessage);
}

我不知道如何将logger函数传递给TqTimingPoint构造函数。

TqTimingPoint tp("testCom", &pLogger->log);

抱怨ISO C++ forbids taking the address of a bound member function to form a pointer to member function. Say ‘&TheLogger::log’

同时尝试:

TqTimingPoint tp("testCom", &TheLogger::log)

no matching function for call to ‘TqTimingPoint::TqTimingPoint(const char [8], void (TheLogger::*)(const char*, ...))’

我希望TimingPoint类可以接受任何具有相同varargs签名的记录器函数。不必在其中的任何地方对TheLogger::进行硬编码。

所以,我想我需要在main()中强制转换TheLogger :: log,但是我都没有尝试过的组合...

(如果在全局范围内定义了logger函数,则可以正常工作。只有类中的log方法存在问题)

编辑我

我之前没有提到的一些限制条件:

a)记录器类不是“我的”,无法修改。因此坚持使用它提供的签名。 (并且真正的logger类更加复杂,是的,确实使用了非静态的log()成员

b)编译器为gcc -std = cxx + 11

c)TqTimingPoint无法识别特定的记录器类(中的硬编码),因为存在一个不相关且不相关的记录器AnotherLogger,它们恰好具有相同的签名

5 个答案:

答案 0 :(得分:0)

  

铸造指向成员函数的函数指针

     

所以,我想我需要在main()中强制转换TheLogger :: log

这里没有演员会帮助您。

指向函数的指针不能指向非静态成员函数。只有指向该类型的成员函数的指针才能执行此操作。成员函数的指针不能转换为函数的指针。

  

我希望TimingPoint类可以接受任何具有相同varargs签名的记录器功能。

TheLogger::log没有相同的签名,因为它是一个非静态成员函数。对于在其上调用函数的对象,即this,有一个隐式参数。


鉴于TheLogger::log不使用任何非静态成员变量,因此不清楚为什么它是非静态成员函数。如果没有必要,一个更简单的解决方案是将其设置为静态:

class TheLogger
{
public:
    static void log(const char* format, ...)

它甚至可能是非成员函数。

答案 1 :(得分:0)

经过一番搜索,它突然出现在我身上:问题当然是隐式this。 每个成员函数都有一个隐式的this成员,这意味着您必须包括源类的类型说明,以便接收指针知道您实际传入的内容(编译器知道它要为其分配内存的内容)。 / p>

完成此工作的唯一方法是使源函数和接收函数静态化,或者将其与继承混合使用,以便您可以将基类函数指针Base::foo传递给虚拟函数,并对其进行求值。任何任意Derived::foo

答案 2 :(得分:0)

另一种方法是使用std::function并使用lambda捕获进行初始化:

struct TheLogger {
    void log(const char* format, ...);
};

struct TqTimingPoint {
    std::string name;
    std::function<void(char const*)> logger_fn;

    void dump() { logger_fn( name.c_str() ); }
};

int main() {
    TheLogger logger;
    TqTimingPoint tp{"testCom", [&logger](char const* arg) { logger.log(arg); }};
}

答案 3 :(得分:0)

问题是,如果您的类的方法既不是静态的也不是虚拟的,那么为成为类A的成员而调用方法a.f(x)大致等同于调用A::f(&a,x)(不,该代码将无法正常工作,但您已经有了主意)。可以使用的代码为(a.*(&A::f))(x);

this隐藏的事实不应使您感到困惑。这正是编译器告诉您“不行不行”的原因。

因此,最合乎逻辑的方法是将引用传递给记录器类实例。如果您想要方便,可以执行以下操作:

class Logger
{
public:
    void operator ()(const char * fmt, ...)
    {
      // do your business here
    }
};

并将您的构造函数更改为

TqTimingPoint(const std::string &name, Logger & log )

您可以将记录器函数设为静态(然后可以传递其地址),但是与将指针传递给独立函数没有什么不同,因为静态函数对类一无所知。

答案 4 :(得分:0)

您不能使用指向成员函数的指针来调用成员函数 just ;您还需要提供指向其所属类的指针。

将成员函数的指针投射到常规函数的指针是未定义行为,并且可能会导致程序崩溃。

有一种更简单的方法来做事情。

用虚拟功能包装记录器

让我们看看记录器基类是什么样的:

class LoggerBase {
   public:
    virtual void log(const char* text, size_t size) const = 0;
    virtual ~LoggerBase() {}
};

该基类具有日志功能;没什么其他的了。现在让我们来看一下使其通用。此类将包含特定的记录器(例如TheLogger),并且仅在其上调用log

template<class Logger>
class SpecificLogger : public LoggerBase {
   private:
    Logger* logger; //Pointer to actual logger
   public:
    SpecificLogger(Logger& logger) : logger(&logger) {}

    void log(const char* text, size_t size) const override {
        logger->log(text, size); 
    }
};

我们可以通过编写一个小的包装器类来使其易于使用并避免动态分配。该WrapperClass接受任何内容,并将其填充到SpecificLogger中。

class LoggerWrapper {
   private:
    struct alignas(sizeof(SpecificLogger<LoggerBase>)) {
        char data[sizeof(SpecificLogger<LoggerBase>)]; 
    };
    LoggerBase const& getLogger() const {
        return *(LoggerBase const*)data; 
    }
   public:
    template<class Logger>
    LoggerWrapper(Logger& logger) {
        new (data) SpecificLogger<Logger>(logger); 
    }
    void log(const char* text, size_t size) const {
        getLogger().log(text, size); 
    }
};

现在TqTimingPoint真的很容易写。

class TqTimingPoint
{
   private:
    std::string   mName;   // Name of this collector
    LoggerWrapper mLogger;             

   public:
    // Logger can be *anything* with a log function 
    // that takes a const char* and a size
    template<class Logger>
    TqTimingPoint(const std::string &name, Logger& logger) :
        mName(name),
        mLogger(logger)
    { }

    void dump()
    {
        mLogger.log(mName.data(), mName.size()); 
    }
};

我们现在可以在完全独立的基类中使用TheLogger,而不必知道基类:

class TheLogger
{
public:
    TheLogger() {} 
    void log(const char* text, size_t size)
    {
        fwrite(text, 1, size, stdout); 
    }
};