微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

在 C++ 类中包装老式 C 库以及没有用户参数的回调

如何解决在 C++ 类中包装老式 C 库以及没有用户参数的回调

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

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

我写了一个极简的片段来抽象我的问题。
在c++版本的并行使用的输出中,可以看到结果是错误的。

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

这是:

#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,10 ) ;
    lib_init( &l2,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++ 类的并行使用打印值 >= 200。
这意味着两个实例都使用了第二个回调...

(我将所有类声明为 struct 以避免public/private 问题是这个片段)

解决方法

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

//---------------------------------------------------------------------
//-- 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,true },static funcs_t funcs [] = { FOR_ALL(FUNC_STRUCT) {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

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。