在 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
我继承了一个需要回调的老式 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