3

次の古いC++コードをアップグレードして、最新のXcode 4.xコンパイラでコンパイルすると、元の開発者が機能するプログラミングイディオムを使用していたことがわかりましたが、回避する必要がありました。これはエラーになります。

*((USHORT*) pvPixel)++ = uRG;   // copy red & green (2 bytes)

意図した内容がわかります。unsignedshort(uRG)をvoidポインター(pvPixel)が指すアドレスにコピーしてから、適切なサイズ(上記の場合は2バイト)ずつインクリメントします。問題は、pvPixelをキャストすると、左辺値ではなく一時的な値になり、許可されなくなることです。

元の開発者は、このイディオムを数十の場所で使用していました。これは、短時間で書き直すことができました。ただし、発生するたびにブルートフォースで書き直すのではなく、エレガントで読みやすいソリューションを提供することをお勧めします。マクロ、インライン関数、おそらくテンプレートなど、各オカレンスをブルートフォースで書き直すためのいくつかの可能な代替案を考えることができます。

私の質問は:この問題に対するC ++構文/言語の解決策はありますか?将来の開発者にとって最も明確なコードを生み出すアプローチは何でしょうか?

(以下の2つの関数例):

void PrimSurfaceGDI3::mFillHLine( UINT uRGB, UINT uX, UINT uY, UINT uW )
{
    LPVOID pvPixel;

    if ( uW > 0 )
    {
        //  obtain a pointer to a specified pixel in the surface
        pvPixel = mPtr( uX, uY );

        USHORT uRG = *(USHORT*) &uRGB;

        BYTE uB = ((BYTE*) &uRGB)[2];

        LPVOID pvEnd = (BYTE*) pvPixel + uW * 3;

        while (pvPixel < pvEnd)
        {
            // The two lines below are now ILLEGAL in modern compilers because casting pvPixel to USHORT* or BYTE* results in a TEMPORARY, not an lvalue
            *((USHORT*) pvPixel)++ = uRG;   // copy red & green (2 bytes)

            *((BYTE*) pvPixel)++ = uB;      // copy blue (1 byte)
        }
    }
}

以下では、このイディオムはforループの再初期化ステートメントで使用されます。

void PrimSurfaceGDI3::mFillVLine( UINT uRGB, UINT uX, UINT uY, UINT uH )
{
    LPVOID pvPixel = mPtr( uX, uY );

    USHORT uRG = *(USHORT*) &uRGB;

    BYTE uB = ((BYTE*) &uRGB)[2];

    LPVOID pvEnd = (BYTE*) pvPixel + uH * muScan;

    // The reinitialization statement is now ILLEGAL in modern compilers because casting pvPixel to BYTE* results in a TEMPORARY, not an lvalue
    for ( ; pvPixel < pvEnd; ((BYTE*) pvPixel) += muScan)
    {
        *(USHORT*) pvPixel = uRG;       // copy red & green (2 bytes)

        ((BYTE*) pvPixel)[2] = uB;      // copy blue (1 byte)
    }
}
4

1 に答える 1

3

おそらく、この問題を解決するためのより適切な方法の1つは、物事を少しやり直すことです。mPtr3つのフィールドを含む3バイトタイプへのポインター(またはコンテナー参照)を返すように変更し、それを使用std::fill_nして入力RGBで埋めます。次に、関数は次のように縮小されます(これにより、コンパイラーに適切な最適化を適用する絶好の機会が与えられます)。

void PrimSurfaceGDI3::mFillHLine( UINT uRGB, UINT uX, UINT uY, UINT uW )
{
    if ( uW > 0 )
    {
        ColorRep pvPixel* = mPtr( uX, uY );
        std::fill_n(pvPixel, uW, ColorRep(uRGB));
    }
}

次に、元の「最適化」は、プログラムに誘導される2バイトのアクセスがすべてずれているため、最適化が解除されている可能性が高いことに注意してください。

ただし、コードの変更をできるだけ少なくしたい場合は、このコードがx86以外のアーキテクチャでは機能しない可能性が高いことを受け入れ(アクセスの位置がずれているため)、コンパイルする意思があります-fno-strict-aliasing(これにより、おそらく無関係なタイプへのキャストのために必要になるでしょう)そしてあなたはおそらくreinterpret_cast参照へのingで逃げることができます:

*(reinterpret_cast<unsigned short*&>(pvPixel))++ = uRG;
于 2013-03-26T20:40:03.290 に答える