在 C++ class 中包装旧式 C 库(以及没有用户参数的回调)

Wrapping old school C lib within C++ class (and a callback without user param)

我继承了一个需要回调的老式 C 库,这个回调没有任何 用户参数

因为我必须在一个不错的 c++ 项目中使用它,所以我写了一个包装 class。
内部库所需的回调被视为只有一个抽象方法的class。
在实现中,我编写了一个调用用户抽象方法的 C 回调。
但是由于这个回调没有任何用户参数,它使用了一个糟糕的全局指针!
由于这个指针被所有实例共享,并行使用是错误的!
但是我不知道如何正确地做...

我写了一个抽象我的问题的极简主义片段。
在c++版本的parallel usage的输出中可以看到结果是错误的

是否有正确的方法来做到这一点,或者因为回调没有用户参数,我注定要失败?

这里是:

#include <iostream>
using namespace std ;

//---------------------------------------------------------------------
//-- code of the old school lib (I can't change it )-------------------

extern "C" {

typedef int (*func_t)(int) ; // callback without any "void * user_param"

typedef struct 
    {
    func_t f ;
    int    i ;
    } t_lib_struct ;

void lib_init ( t_lib_struct * self , func_t f , int i )
    {
    self->f = f ;
    self->i = i ;
    }

void lib_close ( t_lib_struct * self )
    {
    self->f = 0 ;
    self->i = 0 ;
    }

int lib_process ( t_lib_struct * self , int x )
    {
    return self->f( x + self->i ) ;
    }
}

//---------------------------------------------------------------------
//-- old school usage -------------------------------------------------

extern "C" int old_school_func_1 ( int x )
    {
    return x + 100 ;
    }

extern "C" int old_school_func_2 ( int x )
    {
    return x + 200 ;
    }

void old_school_lib_sequential_usage ()
    {
    t_lib_struct l1 ;

    lib_init( &l1,old_school_func_1,10 ) ;
    for ( int i = 0 ; i < 5 ; i++ )
        cout << "   " << lib_process( &l1,i ) ;
    cout << endl ;
    lib_close( &l1 ) ;

    t_lib_struct l2 ;
    lib_init( &l2,old_school_func_2,20 ) ;
    for ( int i = 0 ; i < 5 ; i++ )
        cout << "   " << lib_process( &l2,i ) ;
    cout << endl ;
    lib_close( &l2 ) ;
    }

void old_school_lib_parallel_usage ()
    {
    t_lib_struct l1,l2 ;

    lib_init( &l1,old_school_func_1,10 ) ;
    lib_init( &l2,old_school_func_2,20 ) ;
    for ( int i = 0 ; i < 5 ; i++ )
        cout << "   " << lib_process( &l1,i ) << " // " << lib_process( &l2,i ) << endl ;
    lib_close( &l1 ) ;
    lib_close( &l2 ) ;
    }

void old_school_lib_usage ()
    {
    cout << "sequential:" << endl ;
    old_school_lib_sequential_usage() ;
    cout << "parallel:" << endl ;
    old_school_lib_parallel_usage() ;
    }

//---------------------------------------------------------------------
//-- c++ wrapper ------------------------------------------------------

struct Lib
    {
    struct LibFunc
        {
        virtual int func ( int x ) const = 0 ;
        };
    Lib ( const LibFunc & f , int i ) ;
   ~Lib () ;
    int process ( int x ) ;
    //protected:
    t_lib_struct lib ;
    const LibFunc & f ;
    };

//---------------------------------------------------------------------

Lib * global_lib = 0 ;

extern "C" int wrapped_func ( int x )
    {
    if (!global_lib) return -1 ;
    return global_lib->f.func( x ) ;
    }

Lib::Lib ( const LibFunc & f , int i ) : f(f)
    {
    global_lib = this ;
    lib_init( &lib,wrapped_func,i ) ;
    }

Lib::~Lib ()
    {
    lib_close( &lib ) ;
    global_lib = 0 ;
    }

int Lib::process ( int x )
    {
    return lib_process( &lib,x ) ;
    }

//---------------------------------------------------------------------
//-- c++ style usage --------------------------------------------------

struct MyFunc : Lib::LibFunc
    {
    int d ;
    MyFunc ( int d ) : d(d) {}
    int func ( int x ) const
        {
        return x + d ;
        }
    };

void cpp_lib_sequential_usage ()
    {
    Lib l1( MyFunc( 100 ),10 ) ;
    for ( int i = 0 ; i < 5 ; i++ )
        cout << "   " << l1.process( i ) ;
    cout << endl ;

    Lib l2( MyFunc( 200 ),20 ) ;
    for ( int i = 0 ; i < 5 ; i++ )
        cout << "   " << l2.process( i ) ;
    cout << endl ;
    }

void cpp_lib_parallel_usage ()
    {
    Lib l1( MyFunc( 100 ),10 ),l2( MyFunc( 200 ),20 ) ;
    for ( int i = 0 ; i < 5 ; i++ )
        cout << "   " << l1.process( i ) << " // " << l2.process( i ) << endl ;
    }

void cpp_lib_usage ()
    {
    cout << "sequential:" << endl ;
    cpp_lib_sequential_usage() ;
    cout << "parallel:" << endl ;
    cpp_lib_parallel_usage() ;
    }

//---------------------------------------------------------------------

int main ()
    {
    cout << "==== old school ===================" << endl ;
    old_school_lib_usage() ;
    cout << "==== c++ style ====================" << endl ;
    cpp_lib_usage() ;
    }

并且输出:

==== old school ===================
sequential:
   110   111   112   113   114
   220   221   222   223   224
parallel:
   110 // 220
   111 // 221
   112 // 222
   113 // 223
   114 // 224
==== c++ style ====================
sequential:
   110   111   112   113   114
   220   221   222   223   224
parallel:
   210 // 220
   211 // 221
   212 // 222
   213 // 223
   214 // 224

顺序使用没问题,但您可以看到并行使用 c++ class 打印值 >= 200。
这意味着第二次回调被两个实例使用...

(我将所有 classes 声明为 struct 以避免 public/private issues is this snippet)

对于那些有兴趣的人,这是我解决自己问题的方法:
我使用一组 extern C 函数来代替我旧学校库中缺少的 user 参数
这种方法的主要限制是并发实例数被限制为静态任意数。

//---------------------------------------------------------------------
//-- the c++ wrapper API

class Lib
    {
    public:
        struct Functor
            {
            virtual int func ( int x ) const = 0 ;
            };

        Lib ( const Functor & f , int i ) ;
       ~Lib () ;
        int process ( int x ) ;

    protected:
        struct PirvateLib ;
        PirvateLib * m ;
    };

//---------------------------------------------------------------------
//-- wrapper usage

struct MyFunctor : Lib::Functor
    {
    int d ;
    MyFunctor ( int d ) : d(d) {}
    int func ( int x ) const
        {
        return x + d ;
        }
    };

#include <iostream>

int main ()
    {
    Lib l1( MyFunctor( 100 ),10 ) ;
    Lib l2( MyFunctor( 200 ),20 ) ;
    Lib l3( MyFunctor( 300 ),30 ) ;
    for ( int i = 0 ; i < 5 ; i++ )
        std::cout << "   " << l1.process( i ) << " // " << l2.process( i ) <<  " // " << l3.process( i ) << std::endl ;
    }

//---------------------------------------------------------------------
//-- code of the old school lib (I can't change it)

extern "C" {

    typedef int (*func_t)(int) ; // callback without any "void * user_param"

    typedef struct 
        {
        func_t f ;
        int    i ;
        } t_lib_struct ;

    void lib_init ( t_lib_struct * self , func_t f , int i )
        {
        self->f = f ;
        self->i = i ;
        }

    void lib_close ( t_lib_struct * self )
        {
        self->f = 0 ;
        self->i = 0 ;
        }

    int lib_process ( t_lib_struct * self , int x )
        {
        return self->f( x + self->i ) ;
        }
    }

//---------------------------------------------------------------------
//-- the not-very-clean-solution: a global pool of functions that takes the place of the missing user-param

static const Lib::Functor * get ( int i ) ;

struct funcs_t
    {
    func_t               func ;
    const Lib::Functor * lib_functor ;
    bool                 isfree ;
    } ;

#define FOR_ALL(f) f(0)f(1)f(2)f(3)f(4)f(5)f(6)f(7)f(8)f(9)f(10)f(11)f(12)f(13)f(14)f(15)  // if necessary, add f(16)f(17)...

// create a pool of 16 functions...
extern "C" {
#define FUNC_DEF(i)    static int wrapped_func_##i ( int x ) { return get(i)->func(x) ;}
FOR_ALL(FUNC_DEF)
}

// ....and an associated array of structs (terminated by a "null" element)
#define FUNC_STRUCT(i) { wrapped_func_##i , 0 , true },
static funcs_t funcs [] = { FOR_ALL(FUNC_STRUCT) {0,0,false} } ;

static int alloc () // return the index of a free slot, or -1
    {
    for ( int i = 0 ; funcs[i].func ; i++ )
        if (funcs[i].isfree)
            return funcs[i].isfree = false || i ;
    return -1 ; // allocation error not managed!
    }

static void free ( int i )                
    { 
    funcs[i].isfree = true ;
    }

static const Lib::Functor * get ( int i ) 
    { 
    return funcs[i].lib_functor ;
    }

//---------------------------------------------------------------------
//-- wrapper implementation

struct Lib::PirvateLib
    {
    t_lib_struct lib ;
    int          i ;
    };

Lib::Lib ( const Functor & f , int i ) : m ( new Lib::PirvateLib )
    {
    m->i = alloc() ;
    funcs[m->i].lib_functor = &f ;
    lib_init( &m->lib,funcs[m->i].func,i ) ;
    }

Lib::~Lib ()
    {
    lib_close( &m->lib ) ;
    free( m->i ) ;
    delete m ;
    }

int Lib::process ( int x )
    {
    return lib_process( &m->lib,x ) ;
    }

并且输出:

110 // 320 // 330
111 // 321 // 331
112 // 322 // 332
113 // 323 // 333
114 // 324 // 334