1

我有一个场景,其中我有很多具有以下模式的功能

RETURN_TYPE FOO(
    TYPE PARM1,
    TYPE PARM2)
    {
    PROLOG(PARM1, PARM2);
    //FOO_BODY
    EPILOG(PARM1, PARM2);
    }

考虑一个遵循上述模式的示例函数

SQLRETURN SQLGetInfo(
    SQLHDBC         ConnectionHandle,
    SQLUSMALLINT    InfoType,
    SQLPOINTER      InfoValuePtr,
    SQLSMALLINT     BufferLength,
    SQLSMALLINT *   StringLengthPtr)
    {
    // ******** Prolog(InfoValuePtr, BufferLength) *************
    CComSafeArray<BYTE> _InfoValuePtr(BufferLength);
    LPBYTE pData;
    // **********************************************************
    SQLRETURN  rc =  p->SQLGetInfo(
        reinterpret_cast<PSSQLHSTMT>(ConnectionHandle),
        InfoType,
        _InfoValuePtr,
        BufferLength,
        StringLengthPtr);
    // ******** Epilog(InfoValuePtr) ******************************
    ::SafeArrayAccessData(_InfoValuePtr, reinterpret_cast<void **>(&pData));
    memcpy_s(InfoValuePtr, BufferLength, pData, _InfoValuePtr.GetCount());
    ::SafeArrayUnaccessData(_InfoValuePtr);
    return rc;
    // *************************************************************
    }

我的困境是,我有点不习惯一遍又一遍地重复相同的代码模式,这在开发过程中很容易出错,而且代码臃肿。即使在明天,改变某些东西实际上意味着一丝不苟地改变每一次发生的事情,以与变化保持一致。

处理模式的建议方式/最佳实践是什么?Macros,Templates

注意我们仍然没有使用 boost 并且添加 boost 只是为了解决这个问题在这里可能不是一个选项

4

1 回答 1

1

@Abhijit:我自己的直觉也倾向于模板。但是,您上面的示例看起来并不像您在上面给出的简化模型。如果你确实有大量的函数看起来除了一些中间部分是一样的,那么模板可以清理它们。使您的模板参数成为一个函数指针,指向一个与序言和结语挂钩的函数。这几乎是柯里化的一种变体,但不完全是。

在不完全理解上面的示例的情况下,以下是如何将其转换为使用模板的草图。由于您的示例的主体只是一个函数调用,因此这种转换并没有真正暴露模式的力量。但是,它应该希望给出一个想法。

// Define the "body function" in a typedef, for sanity's sake
typedef SQLRETURN 
            (*SQLGetInfoFunc)(
                PSSQLHSTMT, 
                SQLUSMALLINT,
                CComSafeArray<BYTE>,
                SQLSMALLINT, 
                SQLSMALLINT*
            );  

// Templated version of SQLGetInfo
template <SQLGetInfoFunc *F>
SQLRETURN SQLGetInfo(
    SQLHDBC         ConnectionHandle,
    SQLUSMALLINT    InfoType,
    SQLPOINTER      InfoValuePtr,
    SQLSMALLINT     BufferLength,
    SQLSMALLINT *   StringLengthPtr)
    {
    // ******** Prolog(InfoValuePtr, BufferLength) *************
    CComSafeArray<BYTE> _InfoValuePtr(BufferLength);
    LPBYTE pData;
    // **********************************************************
    SQLRETURN  rc =  F(
        reinterpret_cast<PSSQLHSTMT>(ConnectionHandle),
        InfoType,
        _InfoValuePtr,
        BufferLength,
        StringLengthPtr);
    // ******** Epilog(InfoValuePtr) ******************************
    ::SafeArrayAccessData(_InfoValuePtr, reinterpret_cast<void **>(&pData));
    memcpy_s(InfoValuePtr, BufferLength, pData, _InfoValuePtr.GetCount());
    ::SafeArrayUnaccessData(_InfoValuePtr);
    return rc;
    // *************************************************************
    }

在此示例中,F您将提供一个函数指针作为模板参数。也就是说,我注意到您使用的原始示例p->SQLGetInfo,但我没有看到在哪里p定义。

为了提供显示基本模式的完整示例,请考虑以下代码:

#include <iostream>

typedef int (*fxn)(int);

inline int fred(int x) 
{
    return x + 100;
}

inline int barney(int x)
{
    return x + 200;
}


template <fxn F>
void apply_to_array(int *array, int len)
{
    for (int i = 0; i < len; i++)
        array[i] = F(array[i]);
}

using namespace std;

void print_array(int *array, int len)
{
    for (int i = 0; i < len; i++)
        cout << " " << array[i];

    cout << endl;
}


int af[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int ab[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

int main(void)
{
    cout << "Before: af = ";
    print_array(af, 10);

    apply_to_array<fred>(af, 10);

    cout << "After apply_to_array<fred>(af):  af = ";
    print_array(af, 10);

    cout << "Before: ab = ";
    print_array(ab, 10);

    apply_to_array<barney>(ab, 10);

    cout << "After apply_to_array<barney>(ab):  ab = ";
    print_array(ab, 10);

    return 0;
}

它输出以下内容:

Before: af =  1 2 3 4 5 6 7 8 9 10
After apply_to_array<fred>(af):  af =  101 102 103 104 105 106 107 108 109 110
Before: ab =  1 2 3 4 5 6 7 8 9 10
After apply_to_array<barney>(ab):  ab =  201 202 203 204 205 206 207 208 209 210
于 2013-09-15T08:44:56.620 回答