OpenVX安全关键应用编程实践
1. 引言
在OpenVX编程中,存在一些容易引发问题的情况,比如指针和整数混淆。当类型由属性常量隐含时,这种混淆很容易发生。为了解决这个问题,可以创建模板来映射
vx_enum
到所需的类型,并将特定数据类型和其大小的要求隐藏在模板函数中。这样可以在编译时进行检查,并且要求属性作为常量传递。
2. 引用数组的使用与安全处理
在导出和导入对象时,会使用通用引用数组。但如果使用不当,这些数组可能会成为问题的潜在来源。为了防止错误使用,建议将其使用限制在代码的特定位置。
例如,定义了一个
VxRefArray
类,将所有可能不安全的操作集中在一处。使用
std::vector
来存储引用,可以实现类型安全的函数,将
VxReference
派生类型及其使用方式放入数组并从中取出,同时确保跟踪引用计数。虽然类型检查是在运行时进行的,但至少有了检查机制。
class VxRefArray
{
// Used to handle arrays of references for VxImport factory defined
// in VxContext
private:
std::vector<vx_reference> ref_array;
std::vector<vx_enum> use_array;
operator vx_reference*() { return ref_array.data(); }
operator const vx_enum*() { return use_array.data(); }
// We don’t allow copies of this.
// If for some reason an assignment operator is
// required, then remember to call vxRetainReference
// for each non-nullptr entry.
VxRefArray& operator=( VxRefArray& ) {}
friend class VxContext;
public:
VxRefArray( vx_size numrefs )
{
ref_array.resize( numrefs );
use_array.resize( numrefs );
}
~VxRefArray()
{
for ( vx_reference ref : ref_array )
{
if ( nullptr != ref )
{
vxReleaseReference( &ref );
}
}
}
vx_status put( vx_size index, const VxReference& obj, vx_enum use
= VX_IX_USE_EXPORT_VALUES )
{
vx_status status = VX_SUCCESS;
if ( index < ref_array.size() )
{
if ( nullptr != ref_array[index] )
{
vxReleaseReference( &ref_array[index] );
}
ref_array[index] = obj;
use_array[index] = use;
if ( nullptr != ref_array[index] )
{
vxRetainReference( ref_array[index] );
}
}
else
{
status = VX_FAILURE;
}
return status;
}
template <class TYPE>
TYPE getRef( vx_size index )
{
// Only allowed to get VxReference & derived types from a
// VxRefArray
static_assert( std::is_base_of<VxReference, TYPE>::value,
"VxRefArray::get<TYPE> : TYPE must be derived "
"from VxReference" );
if ( index < ref_array.size() && ( nullptr !=
ref_array[index] ) )
{
vxRetainReference( ref_array[index] );
return TYPE( ref_array[index] );
}
else
{
return TYPE( ( vx_reference ) nullptr );
}
}
vx_enum getUse( vx_size index ) const
{
return ( index < use_array.size() ) ? use_array[index] :
VX_IX_USE_APPLICATION_CREATE;
}
vx_size getSize() const { return ref_array.size(); }
};
3. 实际示例:创建C++类封装OpenVX C实现
为了隐藏OpenVX C实现并处理引用计数,创建了一系列C++类。以下是这些类的详细介绍:
3.1
VxReference
类
将
vx_reference
(一个指针)封装在
VxReference
类中,并通过定义复制构造函数、赋值运算符和析构函数来处理引用计数。在多个地方检查
nullptr
,如果发现
nullptr
,可能是编程错误,可以抛出异常或记录错误以便调试。
class VxReference
{
public:
// Get the context from this reference
VxContext getContext();
// There are no virtual functions in this class.
// Objects of type VxReference may not be instantiated.
protected:
// remember that the OpenVX C definition of vx_reference is a
// pointer to an opaque type. Derived classes will need to be able to
// access the implementation pointer, but the application using this
// shim should not.
vx_reference pimpl;
// Any of the derived classes and friends may cast VxReference or
// a derived class to vx_reference.
operator const vx_reference() const { return
reinterpret_cast<const vx_reference>( pimpl ); }
public:
template <vx_enum A>
typename tagmap<A>::vx_type queryReference(typename
tagmap<A>::vx_type init={0}) const
{
static_assert( (tag & VX_TYPE_MASK) == (VX_TYPE_REFERENCE <<
8), "You must use a Reference type attribute!");
auto data(init);
if ( nullptr != pimpl )
{
vxQueryReference( pimpl, A, &data, sizeof( data ) );
}
return data;
}
protected:
// Protected constructor. VxReference can’t be made without a pimpl.
// pimpl must match given type enum or it’s set to nullptr
VxReference( vx_reference ref, vx_enum vxtype )
: pimpl( ref )
{
// In this constructor we make sure that the reference count of
// the OpenVX object is decremented if the pointer is the wrong
// type
if ( nullptr != pimpl )
{
vx_enum the_type = queryReference<VX_REFERENCE_TYPE>();
if ( the_type != vxtype )
{
vxReleaseReference( &ref );
pimpl = nullptr;
}
}
}
~VxReference( void )
{
// Protected destructor. VxReference objects are not allowed!
// In this destructor we make sure that the underlying OpenVX
// object has its reference count decremented, so that the
// implementation will destroy it when we’ve removed all our
// references to it. Notice we take care never to pass a nullptr
// pointer to any OpenVX APIs. We rely upon the OpenVX API to
// call the correct ReleaseXXX function when
// VxReleaseReference is called.
if ( nullptr != pimpl )
{
vxReleaseReference( &pimpl );
}
}
VxReference( const VxReference& obj )
{
// In the copy constructor we make sure that the reference count
// of the OpenVX object is incremented when we take a copy of
// the pointer
pimpl = obj.pimpl;
if ( nullptr != pimpl )
{
vxRetainReference( pimpl );
}
}
friend class VxGraph;
friend class VxRefArray;
friend class VxContext;
public:
void operator=( const VxReference& obj )
{
// We define the assignment operator so as to be able to keep
// track of the reference counts. An alternative would be to
// raise an error if assigning to a reference that already has
// a value in pimpl since this is most likely an error.
// Notice that the operator does not return a value and hence
// constructs of the type a = b = c cannot be made.
if ( nullptr != pimpl )
{
vxReleaseReference( &pimpl );
}
pimpl = obj.pimpl;
if ( nullptr != pimpl )
{
vxRetainReference( pimpl );
}
}
// getName() is included for utility
const std::string getName() const
{
return queryReference<VX_REFERENCE_NAME>("");
}
vx_status setName( const std::string& name ) { return
vxSetReferenceName( pimpl, name.c_str() ); }
// The vxHint function allows implementations to define various
// hints taking different data types; However, they must first
// define the corresponding tagmap struct to map the hint to
// the data type.
template <enum vx_hint_e attribute>
vx_status hint(typename tagmap<attribute>::vx_type & data )
{
return vxHint( pimpl, attribute, static_cast<void*>( data ),
sizeof( data ) );
}
// Other VxReference methods simply match directly to the OpenVX
// API:
vx_status directive( enum vx_directive_e directive ) { return
vxDirective( pimpl, directive ); }
vx_status getStatus( void ) const { return vxGetStatus( pimpl ); }
};
3.2
VxReference
的扩展:考虑容器问题
如果怀疑实现中容器处理存在不足,即销毁容器会销毁包含的对象,即使存在对它们的引用,可以考虑对
VxReference
对象进行修改,使其持有对父对象的引用。这意味着构造函数会有一个额外的参数,但由于构造函数不是直接调用,而是由父对象的工厂函数调用,所以包装后的API不会有明显变化。不过,会增加一个数据成员,使
VxReference
对象的内存占用翻倍。构造函数必须调用
vxRetainReference()
来保留父对象的引用,析构函数必须调用
vxReleaseReference
来释放父对象的引用。
3.3
queryReference
和
tagmap
模板
在
queryReference()
成员函数的定义中,使用了
tagmap
模板。
tagmap
模板将标签与类型关联起来,用于属性和类型的一般映射。以下是一些
tagmap
模板的定义:
// a tagMap associates a tag with a type, used for attributes and types
// generally
template <vx_enum t> struct tagmap {};
template <> struct tagmap<VX_TYPE_CHAR> { typedef vx_char vx_type;};
template <> struct tagmap<VX_TYPE_INT8> { typedef vx_int8 vx_type;};
template <> struct tagmap<VX_TYPE_UINT8> { typedef vx_uint8 vx_type;};
template <> struct tagmap<VX_TYPE_INT16> { typedef vx_int16 vx_type;};
template <> struct tagmap<VX_TYPE_UINT16> { typedef vx_uint16 vx_type;};
template <> struct tagmap<VX_TYPE_INT32> { typedef vx_int32 vx_type;};
template <> struct tagmap<VX_TYPE_UINT32> { typedef vx_uint32 vx_type;};
template <> struct tagmap<VX_TYPE_INT64> { typedef vx_int64 vx_type;};
template <> struct tagmap<VX_TYPE_UINT64> { typedef vx_uint64 vx_type;};
template <> struct tagmap<VX_TYPE_FLOAT32> { typedef vx_float32
vx_type;};
template <> struct tagmap<VX_TYPE_FLOAT64> { typedef vx_float64
vx_type;};
template <> struct tagmap<VX_TYPE_ENUM> { typedef vx_enum vx_type;};
template <> struct tagmap<VX_TYPE_SIZE> { typedef vx_size vx_type;};
template <> struct tagmap<VX_TYPE_DF_IMAGE> { typedef vx_df_image
vx_type;};
template <> struct tagmap<VX_TYPE_BOOL> { typedef vx_bool vx_type;};
template <> struct tagmap<VX_REFERENCE_TYPE> { typedef vx_enum vx_type;};
template <> struct tagmap<VX_REFERENCE_NAME> { typedef const vx_char*
vx_type;};
template <> struct tagmap<VX_REFERENCE_COUNT> { typedef vx_uint32
vx_type;};
template <> struct tagmap<VX_GRAPH_STATE>
{ typedef vx_enum vx_type;};
template <> struct tagmap<VX_GRAPH_NUMNODES> { typedef vx_uint32
vx_type;};
template <> struct tagmap<VX_GRAPH_NUMPARAMETERS> { typedef vx_uint32
vx_type;};
template <> struct tagmap<VX_GRAPH_PERFORMANCE> { typedef vx_perf_t
vx_type;};
template <> struct tagmap<VX_IMAGE_WIDTH>
{ typedef vx_uint32
vx_type;};
同时,还定义了一些其他模板,如
const_if_RO
模板,用于根据传递给某些OpenVX函数的“使用”常量来改变类型:
// Add const to a type T if tag is VX_READ_ONLY
template <typename T, vx_enum tag> struct const_if_RO
{
typedef T vx_type;
typedef T * vx_ptr;
typedef T & vx_ref;
};
template <typename T> struct const_if_RO<T, VX_READ_ONLY>
{
typedef const T vx_type;
typedef const T * vx_ptr;
typedef const T & vx_ref;
};
4. 总结
通过上述的类和模板的定义,我们可以更安全地使用OpenVX对象,避免一些常见的错误。
VxReference
类及其派生类可以帮助我们管理引用计数,
VxRefArray
类可以安全地处理引用数组,而
tagmap
模板和相关模板可以在编译时进行类型检查,提高代码的安全性和可靠性。
以下是一个简单的流程图,展示了
VxRefArray
类的主要操作流程:
graph TD;
A[创建VxRefArray对象] --> B[初始化引用数组和使用数组];
B --> C[放入引用和使用信息];
C --> D{是否成功放入};
D -- 是 --> E[保留引用计数];
D -- 否 --> F[返回失败状态];
E --> G[获取引用和使用信息];
G --> H[释放引用计数];
H --> I[销毁VxRefArray对象];
在实际使用中,可以按照以下步骤操作:
1. 创建
VxRefArray
对象,指定引用数量。
2. 使用
put
方法将
VxReference
派生类型及其使用方式放入数组。
3. 使用
getRef
方法获取引用。
4. 使用完后,
VxRefArray
对象的析构函数会自动释放所有引用的计数。
通过这些操作,可以确保引用计数的正确管理,避免内存泄漏和其他潜在问题。
OpenVX安全关键应用编程实践
5.
VxContext
类
VxContext
对象在实际使用中是在其他对象之后定义的,因为它包含创建其他对象的工厂函数,所有对象都在这个上下文中创建。
class VxContext : public VxReference
{
private:
VxContext( vx_context ref )
: VxReference( reinterpret_cast<vx_reference>( ref ),
VX_TYPE_CONTEXT )
{
}
operator const vx_context() const { return
reinterpret_cast<vx_context>( pimpl ); }
friend class VxReference;
public:
~VxContext() {}
// Standard constructor uses OpenVX API to create a context
VxContext( void )
: VxReference( reinterpret_cast<vx_reference>( vxCreateContext()
), VX_TYPE_CONTEXT )
{
}
// deployment set Context queries
template <vx_enum A>
typename tagmap<A>::vx_type queryContext(typename
tagmap<A>::vx_type init={0}) const
{
static_assert( (A & VX_TYPE_MASK) == (VX_TYPE_CONTEXT << 8),
"You must use an Context type attribute!");
auto data(init);
if ( nullptr != pimpl )
{
vxQueryContext( *this, A, &data, sizeof( data ) );
}
return data;
}
std::string getImplementation() const
{
vx_char name[VX_MAX_IMPLEMENTATION_NAME];
vxQueryContext( *this, VX_CONTEXT_IMPLEMENTATION, name,
sizeof( name ) );
return name;
}
std::string getExtensions() const
{
vx_size xsize = 0;
vxQueryContext( *this, VX_CONTEXT_EXTENSIONS_SIZE, &xsize,
sizeof( xsize ) );
vx_char name[xsize];
vxQueryContext( *this, VX_CONTEXT_EXTENSIONS, name, xsize );
return name;
}
std::vector<vx_kernel_info_t> getUniqueKernelTable() const
{
vx_uint32 count = queryContext<VX_CONTEXT_UNIQUE_KERNELS>();
std::vector<vx_kernel_info_t> table( count );
vxQueryContext( *this, VX_CONTEXT_UNIQUE_KERNEL_TABLE,
table.data(), count * sizeof( vx_kernel_info_t ) );
return table;
}
vx_status exportObjectsToMemory( VxRefArray& refs, const
vx_uint8*& ptr, vx_size& length )
{
return vxExportObjectsToMemory( *this, refs.getSize(), refs,
refs, &ptr, &length );
}
// Factories for objects of all the other classes are defined here
VxImport importObjectsFromMemory( VxRefArray& refs, const
vx_uint8* ptr, vx_size length )
{
return VxImport( vxImportObjectsFromMemory( *this,
refs.getSize(), refs, refs, ptr, length ) );
}
VxImage createImage( vx_uint32 width, vx_uint32 height,
vx_df_image color ) // Standard image creation
{
return VxImage( vxCreateImage( *this, width, height, color )
);
}
VxImage createUniformImage( vx_uint32 width, vx_uint32 height,
const vx_df_image_e color, const vx_pixel_value_t pixel )
{
return VxImage( vxCreateUniformImage( *this, width, height,
color, &pixel ) );
}
VxImage createImageFromHandle( const VxImageHandle& handle )
{
return VxImage( vxCreateImageFromHandle( *this,
handle.color(), handle, handle, handle.mem_type() ) );
}
VxObjectArray createObjectArray( VxReference& exemplar, vx_size
count )
{
return VxObjectArray( vxCreateObjectArray( *this, exemplar,
count ) );
}
VxDelay createDelay( VxReference& exemplar, vx_size count )
{
return VxDelay( vxCreateDelay( *this, exemplar, count ) );
}
// Create a scalar with compile-time Type
template <vx_enum tag>
VxScalar<tag> createScalar(typename tagmap<tag>::vx_type val)
{
return vxCreateScalar(*this, tag, &val);
}
// Create a Scalar when type not known at compile-time
VxScalar<0> createScalar(vx_enum tag, const void * val)
{
return vxCreateScalar(*this, tag, val);
}
template <vx_enum T>
VxLUT<T> createLUT(vx_size count)
{
return vxCreateLUT(*this, T, count);
}
VxLUT<0> createLUT(vx_enum T, vx_size count)
{
return vxCreateLUT(*this, T, count);
}
// Create an Array with compile-time type
template <vx_enum tag>
VxArray<tag> createArray(vx_size capacity)
{
return vxCreateArray(*this, tag, capacity);
}
// Create an Array when type not known at compile-time
VxArray<0> createArray(vx_enum A, vx_size capacity)
{
return vxCreateArray(*this, A, capacity);
}
// Create a Remap
VxRemap createRemap(vx_uint32 src_width, vx_uint32 src_height,
vx_uint32 dst_width, vx_uint32 dst_height)
{
return vxCreateRemap(*this, src_width, src_height,
dst_width, dst_height);
}
// Create a Pyramid
VxPyramid createPyramid(vx_size levels, vx_float32 scale,
vx_uint32 width, vx_uint32 height, vx_df_image format)
{
return vxCreatePyramid(*this, levels, scale, width, height,
format);
}
// Create a convolution
VxConvolution createConvolution(vx_size columns, vx_size rows)
{
return vxCreateConvolution(*this, columns, rows);
}
};
该类提供了一些专门的非模板函数,用于处理三个上下文属性:
getImplementation()
、
getExtensions()
和
getUniqueKernelTable()
。在部署集中,上下文属性不能被更改,因此没有
setContextAttribute()
函数。
操作步骤如下:
1. 创建
VxContext
对象,可以使用默认构造函数调用
vxCreateContext()
创建上下文。
2. 使用
queryContext
模板函数查询上下文属性。
3. 使用工厂函数创建其他类型的对象,如
VxImage
、
VxObjectArray
等。
6.
VxImport
类
VxImport
类的定义相对简单,构造函数被声明为私有,因为
VxImport
对象实际上是由
VxContext
对象中的工厂函数创建的,所以
VxContext
是该类的友元类。
class VxImport : public VxReference
{
private:
VxImport(vx_import ref) :
VxReference(reinterpret_cast<vx_reference>(ref),
VX_TYPE_IMPORT){}
operator const vx_import() const
{
return reinterpret_cast<vx_import>(pimpl);
}
friend class VxContext;
public:
~VxImport(){}
// factories
template<class TYPE>
TYPE getReferenceByName(const vx_char * name)
{
return TYPE(vxGetImportReferenceByName(*this, name));
}
};
该类唯一的公共函数是一个模板函数,用于包装
vxGetImportReferenceByName
函数调用,并返回正确的类型。使用步骤如下:
1. 通过
VxContext
的
importObjectsFromMemory
工厂函数创建
VxImport
对象。
2. 使用
getReferenceByName
模板函数根据名称获取引用。
7.
VxGraph
类
VxGraph
类封装了OpenVX图对象,该类定义仅适用于部署功能集,因此对图的操作有限。
class VxGraph : public VxReference
{
private:
operator const vx_graph() const { return reinterpret_cast<const
vx_graph>( pimpl ); }
VxGraph( vx_reference ref )
: VxReference( ref, VX_TYPE_GRAPH )
{
}
friend class VxImport;
// Arrays of reference need access to the pimpl
friend class VxRefArray;
public:
~VxGraph() {}
template <vx_enum tag>
typename tagmap<tag>::vx_type queryGraph(typename
tagmap<tag>::vx_type init={0}) const
{
static_assert( (tag & VX_TYPE_MASK) == (VX_TYPE_GRAPH << 8), "You
must use a Graph type attribute!");
auto data(init);
if ( nullptr != pimpl )
{
vxQueryGraph( *this, tag, &data, sizeof( data ) );
}
return data;
}
vx_status setGraphParameterByIndex( vx_uint32 index, VxReference&
value )
{
// 原文档此处代码未完整给出
}
};
该类定义了模板化的
queryGraph
函数,用于查询图属性,但不能直接设置图属性。图可以被处理、调度和等待,但不能被创建,可以从导入对象中获取。操作步骤如下:
1. 从
VxImport
对象中获取
VxGraph
对象。
2. 使用
queryGraph
模板函数查询图属性。
3. 使用
setGraphParameterByIndex
函数设置图参数(如果代码完整)。
8. 总结
通过上述一系列类和模板的定义,我们可以更安全、更方便地使用OpenVX进行编程。以下是这些类的主要功能总结表格:
| 类名 | 主要功能 |
| ---- | ---- |
|
VxReference
| 封装
vx_reference
,处理引用计数,提供查询和设置属性的方法 |
|
VxRefArray
| 安全处理OpenVX对象的引用数组,管理引用计数 |
|
VxContext
| 创建OpenVX上下文,包含创建其他对象的工厂函数 |
|
VxImport
| 从内存中导入对象,提供按名称获取引用的方法 |
|
VxGraph
| 封装OpenVX图对象,提供查询图属性的方法 |
以下是一个简单的流程图,展示了使用这些类创建和操作OpenVX对象的主要流程:
graph LR;
A[创建VxContext对象] --> B[使用VxContext创建其他对象];
B --> C{对象类型};
C -- VxImage --> D[创建图像对象];
C -- VxGraph --> E[获取图对象并查询属性];
C -- VxImport --> F[导入对象并获取引用];
C -- VxRefArray --> G[处理引用数组];
D --> H[使用图像对象];
E --> I[处理图对象];
F --> J[使用导入的对象];
G --> K[管理引用计数];
H --> L[释放对象];
I --> L;
J --> L;
K --> L;
L --> M[销毁VxContext对象];
在实际编程中,我们可以按照这个流程来组织代码,确保OpenVX对象的正确创建、使用和释放,提高代码的安全性和可靠性。
超级会员免费看
15

被折叠的 条评论
为什么被折叠?



