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;
    // *************************************************************
    }

私のジレンマは、開発中にエラーが発生しやすく、コードがかなり肥大化するという同じコードパターンを何度も繰り返すのが少し不快だということです。明日になっても、何かを変えるということは文字通り、その変化に一貫性を持たせるために、すべての出来事を細心の注意を払って変えることを意味します。

パターンを処理するための推奨される方法/ベストプラクティスは何ですか? MacrosTemplates

注:まだブーストを使用しておらず、この問題を解決するためだけにブーストを追加することは、ここでは選択肢にならない可能性があります

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は、 はテンプレート パラメーターとして提供する関数ポインターです。そうは言っても、元の例が used であることに気付きましたが、どこで定義されている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 に答える