【发布时间】:2021-04-08 10:33:55
【问题描述】:
我继承了一个需要回调的老式 C 库,而这个回调没有任何用户参数。
因为我必须在一个不错的 c++ 项目中使用它,所以我编写了一个包装类。
内部库所需的回调被视为只有一个抽象方法的类。
在实现中,我编写了一个调用用户抽象方法的 C 回调。
但是由于这个回调没有任何用户参数,它使用了一个糟糕的全局指针!
而且由于这个指针被所有实例共享,并行使用是错误的!
但我无法弄清楚如何正确地做到这一点......
我写了一个简约的 sn-p 来抽象我的问题。
在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,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++ 类打印值 >= 200。
这意味着两个实例都使用了第二个回调......
(我将所有类声明为 struct 以避免 public/private 问题是这个 sn-p)
【问题讨论】:
-
你用的是什么编译器?海湾合作委员会?
-
像
template <std::size_t> struct Lib;这样的东西怎么样,所以Lib<1> l1;和Lib<2> l2;可以共存(因为它们都有自己的全局)? -
@Jarod42 我在想类似的事情,也许 CRTP
LibFunc为每个实例获取一个新的回调? -
您是否尝试使用类方法作为回调?
标签: c++ c function-pointers wrapper