简体   繁体   English

在静态类函数成员中获取类对象指针

[英]Getting class object pointer in static class function member

There is any "smart" way to get the class object pointer from a static class function member, example: 有任何“智能”方式可从静态类函数成员获取类对象指针,例如:

class my_class
{
public:

    my_class(int a) : v_a(a) {};

    static void static_f()
    {
        my_class* p = GET OBJECT POINTER
        std::cout << p->v_a << std::endl;
    }

private:
    int v_a;
};

I know that it may be impossible but i have been struggling for 2 days And so far i have been able to achieve it using templates and declaring the object in global scope: 我知道这可能是不可能的,但是我已经挣扎了2天,到目前为止,我已经能够使用模板并在全局范围内声明对象来实现它:

    template<my_class* tp>
    static void static_f()
    {
        my_class* p = tp;
        std::cout << p->v_a << std::endl;
    }

  my_class v1(100); // global variable
  my_class v2(200); // global variable

  v1.static_f<&v1>(); // main function
  v2.static_f<&v2>(); // main function

and it prints out 100 and 200 respectly. 并分别打印出100和200。 this wouldnt work if i declare it as local variable though. 如果我将其声明为局部变量,这将行不通。 by "smart" i mean some new feature of c++11 or higher which i am not aware of. “智能”是指我不知道的c ++ 11或更高版本的一些新功能。

Purpose: 目的:

I am trying to encapsule a couple of functions of a C library of which one of them requires a pointer to a function used as callback, this callback function pointer will not be called by myself but by the library and it does not accept additional parameters passed as context so i cannot call it directly ( i did in the code since i wanted to keep the example as small as possible ) or just pass the object pointer. 我试图封装一个C库的几个函数,其中的一个需要指向用作回调函数的指针,这个回调函数指针不会由我自己调用,而是由库调用,并且不接受传递的其他参数作为上下文,所以我不能直接调用它(我在代码中做了,因为我想使示例尽可能小)或仅传递对象指针。

Possible solution: 可能的解决方案:

So far, i have been capable to achieve this by using templates and a global variable per object: 到目前为止,我已经能够通过使用模板和每个对象一个全局变量来实现这一目标:

template<void** pp>
class my_class
{
public:

    my_class(int v1, int v2)
    {
        x_var1 = v1;
        x_var2 = v2;
        *pp = this;
    }

    void call_callback_for_test()
    {
        callback();
    }

private:

    static void callback()
    {
        my_class* p = reinterpret_cast<my_class*>(*pp);
        std::cout << p->x_var1 << " - " << p->x_var2 << std::endl;
    }


    int x_var1;
    int x_var2;
};


void* pv1;
void* pv2;
my_class<&pv1> v1(100, 200);


int main()
{
    my_class<&pv2> v2(300, 400);

    v1.call_callback_for_test();
    v2.call_callback_for_test();
    return 0;
}

YET, i feel that there could be a better solution using newer c++ features for example by using constexpr or metaprogramming ( i read a bit about them, i may be wrong ) but i am just very beginner and inexperienced, or improving this approach. 是的,我觉得使用更新的c ++功能可能会有更好的解决方案,例如通过使用constexpr或元编程(我对此有一点了解,我可能是错的),但是我只是一个初学者,没有经验,或者正在改进这种方法。

It's not too hard to do what you are asking; 要做您要问的事并不难; it may look something like this: 它可能看起来像这样:

class Foo {
private:
    int bar_;
    static Foo* pFoo;

public:
    explicit Foo( int a ) : bar_( a ) {
        pFoo = this;
    }

    static void printBar() {
        std::cout << pFoo->bar_ << '\n';
    }
};

Foo* Foo::pFoo = nullptr;

int main() {
    Foo f( 3 );
    f1.printBar();

    return 0;
}

-Output- -输出-

3

This can work but you have to be cautious of how static pointers work. 这可以工作,但是您必须对静态指针的工作方式保持谨慎。 Take this program for example: 以这个程序为例:

int main() {
    Foo f1( 3 );
    f1.printBar();

    Foo f2( 8 );
    f2.printBar();

    return 0;
}

-Output- -输出-

3
8

Okay so we have 2 instances of Foo ; 好的,我们有2个Foo实例; f1 and f2 and they output 3 and 8 respectively; f1f2分别输出38 so why do we need caution? 那么为什么我们需要谨慎? Let's run the above again one more time but with one line of coded added. 让我们再运行一​​次以上,但是添加一行代码。

int main() {
    Foo f1( 3 );
    f1.printBar();

    Foo f2( 8 );
    f2.printBar();

    // call f1's printBar() again
    f1.printBar(); // What do you think the output will be? 
    // I will tell you that it is not 3!

    return 0;
}

-Output- -输出-

3
8
8

This maybe what you are looking for but you have to be conscience of a class's member static pointer and how they behave with multiple instances of a class. 这也许是您正在寻找的东西,但您必须了解类的成员静态指针以及它们在类的多个实例中的行为。

In the above example f1 's member bar_ was set to 3 in it's constructor then the static pointer was set to this. 在上面的示例中, f1的成员bar_在其构造函数中设置为3 ,然后将静态指针设置为此。 Then when we created f2 and called it's constructor with a value of 8 ; 然后,当我们创建f2并调用它的值为8的构造函数时; it set f2 's bar_ to 8 , and then it set the static pointer again. 它将f2bar_设置为8 ,然后再次设置static pointer This also happens to change f1 's bar from 3 to 8 because of static storage . 由于static storage f1bar3更改为8 This is something you need to be aware of! 这是您需要注意的事情!


Even if you invoke the static method by the class's scope resolution operator after the values have been initialized it will still produce the same result. 即使在值初始化之后由类的范围解析运算符调用静态方法,它仍然会产生相同的结果。

int main() {
    Foo f1( 3 );
    f1.printBar();

    Foo f2( 8 );
    f2.printBar();

    f1.printBar(); 

    Foo::printBar();

    return 0;
}

-Output- -输出-

3
8
8
8

This is just the general idea of how a class's member static pointer works, along with static methods. 这只是类的成员静态指针以及静态方法如何工作的一般思路。


User Alan Birtles had mentioned a caveat or pitfall that I didn't think about when I wrote this answer. 用户Alan Birtles在编写此答案时提到了我没有想到的警告或陷阱。 He stated: 他说:

Note that your callbacks will have undefined behaviour if the most recently created object has been destroyed. 请注意,如果最近创建的对象已被破坏,则回调将具有未定义的行为。

One option would be to use tag structures as your template parameters to generate different types for each callback: 一种选择是使用标记结构作为模板参数,以为每个回调生成不同的类型:

#include <iostream>
#include <string>
#include <memory>

template < typename T >
class Foo
{
public:
    static void test()
    {
        std::cout << instance() << "\n";        
    }

    static std::shared_ptr< Foo< T > > instance()
    {
        static std::shared_ptr< Foo< T > > foo( new Foo< T >() );
        return foo;
    }

private:
    Foo() {}
};

struct instance1 {};
struct instance2 {};

typedef void(*callback_t)();

int main()
{
    callback_t callback1 = Foo< instance1 >::test;
    callback_t callback2 = Foo< instance2 >::test;
    callback1();
    callback2();
}

I've used shared pointers to ensure the created objects still exist when the callback is executed (note these will exist for the life of the program). 我使用共享指针来确保在执行回调时创建的对象仍然存在(请注意,这些对象在程序的生命周期中将一直存在)。 If you want to control the lifetime yourself you could use a weak pointer instead: 如果您想自己控制生命周期,则可以使用弱指针:

#include <iostream>
#include <string>
#include <memory>

template < typename T >
class Foo
{
public:
    static void test()
    {
        std::cout << instance() << "\n";        
    }

    static std::shared_ptr< Foo< T > > instance()
    {
        static std::weak_ptr< Foo< T > > foo;
        std::shared_ptr< Foo< T > > result = foo.lock();
        if ( !result )
        {
            // will need a mutex here in multi-threaded applications
            result.reset( new Foo< T >() );
            foo = result;
        }
        return result;
    }

private:
    Foo() {}
};

struct instance1 {};
struct instance2 {};

typedef void(*callback_t)();

int main()
{
    {
        auto foo = Foo< instance1 >::instance();
        callback_t callback1 = decltype(foo)::element_type::test;
        callback1();
    }
    {
        auto foo = Foo< instance2 >::instance();
        callback_t callback2 = decltype(foo)::element_type::test;
        callback2();
    }
    {
        auto foo = Foo< instance1 >::instance();
        // using a different object to callback1
        callback_t callback3 = decltype(foo)::element_type::test;
        callback3();
    }
}

If the callback is called without a live object then a new object will be created and destroyed rather than crashing the program. 如果在没有活动对象的情况下调用该回调,则将创建并销毁一个新对象,而不是使程序崩溃。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM