C ++减少冗余

时间:2019-03-28 17:57:06

标签: c++ c++11 visual-c++

我专门为Excel调用的DLL中有许多C ++函数。

我经常将这些参数作为包含SafeArrays的OLE VARIANT参数传递给这些函数。

我编写了一个函数来充当防护,以确保传递的VARIANT实际上包含一个SafeArray,并且就数据类型和维数而言,确保该数组具有适合该场合的类型。

如果满足这三个条件,该函数将返回指向第一个元素的指针,并且还具有两个out参数以返回每个维度中的元素数量(注意:我只关心1d和2d SafeArray)。

功能如下:

PVOID SafeArrayDataPointer(VARIANT &v, const long &ArrayType, const long &ArrayDims, long &Elems1D, long &Elems2D) {

    SAFEARRAY* pSafeArray = NULL;

    if ( V_VT(&v) & VT_ARRAY ) {

        if ( ArrayType != (V_VT(&v) & VT_TYPEMASK) ) return NULL;

        pSafeArray = V_ARRAY(&v);
        if ( ArrayDims != pSafeArray->cDims ) return NULL;

        switch (ArrayDims) {
        case 2:
            Elems1D = (pSafeArray->rgsabound)[1].cElements;
            Elems2D = (pSafeArray->rgsabound)[0].cElements;
            break;
        case 1:
            Elems1D = (pSafeArray->rgsabound)[0].cElements;
            Elems2D = 0;
            break;
        default: 
            Elems1D = 0;
            Elems2D = 0;
            break;
        }

        return pSafeArray->pvData;

    } else return NULL;

}

此函数运行良好,并允许我通过这样的调用方便地获取数据指针并获取每个维度中的元素数(假设vData是从Excel传递的变量:

pDataArray = (VARIANT*) SafeArrayDataPointer(vData, VT_VARIANT, 2, Elems1D, Elems2D); if (pDataArray == NULL) goto error1;

但是,我对此有两点不满意:

1。)由于函数返回了PVOID,所以我必须强制转换为相关的指针类型...但这是多余的,因为我已经在第二个参数中指定了必须包含哪种类型的数组。变体。例如,在另一种情况下,我可能需要确保数组具有长值:

pDataArray = (long*) SafeArrayDataPointer(vData, VT_I4, 1, Elems1D, Junk); if (pDataArray == NULL) goto error1;

同样,这可以正常工作,但它是多余的。我更喜欢某种允许该函数返回正确类型的指针的机制。我希望无需模板即可完成此操作。

2。)我无法弄清楚在第二个示例中如何没有Junk参数,在该示例中,我指定数组必须为1d。显然,在这种情况下,第二维中没有元素。

2 个答案:

答案 0 :(得分:1)

如果您不想使用模板,又不想使用专门的功能,则可以使用 gross宏解决必须从PVOID进行强制转换的第一个问题每次您致电SafeArrayVariantPointer

类似这样的东西:

#define CAST_TYPE_VT_VARIANT (VARIANT *)
#define CAST_TYPE_VT_LONG (long *)

#define SAFE_ARRAY_DATA_POINTER(vData, vType, dimSize, elems1D, elems2D) \
    CAST_TYPE_##vType SafeArrayDataPointer((vData), (vType), (dimSize), (elems1D), (elems2D))

然后您可以像这样致电

VARIANT *pDataArray = SAFE_ARRAY_DATA_POINTER(vData, VT_VARIANT, 2, Elems1D, Elems2D);

但是首先您需要更改方法签名,以便将ArrayType参数作为long而不是const long &

请注意,这假设SAFE_ARRAY_DATA_POINTER宏的第二个参数必须是与您定义的CAST_TYPE_ *宏之一相对应的文字。它不能是变量。

对于第二个关于冗余Junk参数的问题,您可以创建一个重载的SafeArrayDataPointer函数,该函数仅返回第一维的大小。它可以调用SafeArrayDataPointer的第一个版本,并丢弃第二个维度的大小。

类似的东西:

PVOID SafeArrayDataPointer(VARIANT &v, long ArrayType, const long &ArrayDims, long &Elems1D) 
{
    long Elems2D;
    PVOID *toReturn = SafeArrayDataPointer(v, ArrayType, ArrayDims, Elems1D, Elems2D);
    if (Elems2D != 0) toReturn = NULL;
    return toReturn;
}

但是,要解决此问题,我可能会使用模板。

首先,创建一组array_type_traits类,这些类针对给定的长类型typedefVT_LONG等为您的演员类型公开VT_VARIANT

//Generic array_type_traits class
template<long array_type>
class array_type_traits 
{
public:
    typedef PVOID cast_type;
};

//Specialized for VT_LONG
template<>
class array_type_traits<VT_LONG>
{
public:
    typedef long * cast_type; 
};

//Specialized for VT_VARIANT
template<>
class array_type_traits<VT_VARIANT>
{
public:
    typedef VARIANT * cast_type;
};

继续针对您拥有的每种VT_ *类型专门化这些。

下一步,将SafeArrayDataPointer函数封装在类SafeArrayDataPointerBase中。

//Base class which has one static function Get() that returns a PVOID
class SafeArrayDataPointerBase
{
protected:
    static PVOID Get(VARIANT& vData, long vType, long dimSize, long& elems1D, long& elems2D)
    {
        // Place your SafeArrayDataPointer function code here 
    }
};

现在创建您的类,该类将调用SafeArrayDataPointerBase :: Get(),然后将结果转换为正确的类型。

template<long ArrayType>
class SafeArrayDataPointer : public SafeArrayDataPointerBase
{
public:
    typedef typename array_type_traits<ArrayType>::cast_type cast_type;

    static cast_type Get(VARIANT& v, long ArrayDims, long& Elems1D, long& Elems2D)
    {
        return (cast_type) SafeArrayDataPointerBase::Get(v, ArrayDims, ArrayType, Elems1D, Elems2D);
    }
};

最后,您将这样调用模板类:

VARIANT *vp = SafeArrayDataPointer<VT_VARIANT>::Get(v, ArrayDims, Elems1D, Elems2D); 
long *vl = SafeArrayDataPointer<VT_LONG>::Get(v, ArrayDims, Elems1D, Elems2D);

答案 1 :(得分:0)

类似this的东西:

template<int vt> struct vt_type_disp;
template<> struct vt_type_disp<VT_I4> { typedef LONG type; };
template<> struct vt_type_disp<VT_VARIANT> { typedef VARIANT type; };

template<int vt> using vt_type = typename vt_type_disp<vt>::type;


template<int vtElem>
auto unpack_array(VARIANT& v, ULONG& d1_size)
{
    if ((V_VT(&v) & VT_ARRAY) && (V_VT(&v) & VT_TYPEMASK) == vtElem)
    {
        SAFEARRAY* p = (V_VT(&v) & VT_BYREF) ? *V_ARRAYREF(&v) : V_ARRAY(&v);

        if (p->cDims == 1)
        {
            d1_size = p->rgsabound[0].cElements;
            return static_cast<vt_type<vtElem>*>(p->pvData);
        }
    }
    return static_cast<vt_type<vtElem>*>(nullptr);
}

template<int vtElem>
auto unpack_array(VARIANT& v, ULONG& d1_size, ULONG& d2_size)
{
    if ((V_VT(&v) & VT_ARRAY) && (V_VT(&v) & VT_TYPEMASK) == vtElem)
    {
        SAFEARRAY* p = (V_VT(&v) & VT_BYREF) ? *V_ARRAYREF(&v) : V_ARRAY(&v);

        if (p->cDims == 2)
        {
            d1_size = p->rgsabound[1].cElements;
            d2_size = p->rgsabound[0].cElements;
            return static_cast<vt_type<vtElem>*>(p->pvData);
        }
    }
    return static_cast<vt_type<vtElem>*>(nullptr);
}

// functions to export from dll (if you need them)
auto unpack_array_I4_1D(VARIANT &v, ULONG& dim_size) { return unpack_array<VT_I4>(v, dim_size); }
auto unpack_array_I4_2D(VARIANT &v, ULONG& d1_size, ULONG& d2_size) { return unpack_array<VT_I4>(v, d1_size, d2_size); }
auto unpack_array_VARIANT_1D(VARIANT &v, ULONG& dim_size) { return unpack_array<VT_VARIANT>(v, dim_size); }
auto unpack_array_VARIANT_2D(VARIANT &v, ULONG& d1_size, ULONG& d2_size) { return unpack_array<VT_VARIANT>(v, d1_size, d2_size); }
// etc

注意:您的代码无法正确处理VT_BYREF