简体   繁体   中英

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:

    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. 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.

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.

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.

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 ; f1 and f2 and they output 3 and 8 respectively; 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. Then when we created f2 and called it's constructor with a value of 8 ; it set f2 's bar_ to 8 , and then it set the static pointer again. This also happens to change f1 's bar from 3 to 8 because of static storage . 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. 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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