0

次の構文を「認める」ことができるのか、それともグッドプラクティスがこれを地獄から来ていると見なすのかどうか疑問に思います。目標は、開発者に自分のしていることを十分に意識させるための保護レベルを追加することです。構文は次のとおりです。

class MyClass
{
    public:
        template<bool RemoveProtection = false> 
        inline std::ofstream& writeStream()
        {
            static_assert(RemoveProtection, "You're doing it wrong");
            return _writeStream;
        }

        inline const std::ofstream& writeStream() const
        {
            return _writeStream;
        }

    protected:
        std::ofstream _writeStream;
};

用途は次のようになります:

x.writeStream().good(); // <- OK
x.writeStream().put('c'); // <- NOT OK
x.writeStream<true>().put('c'); // <- OK

これは、開発者に伝えるための便利な方法だと思います。「注意してください。低レベルの関数を使用しているので、何をしているかに注意する必要があります」。それは、一種の「保護」を備えたクラスメンバーへの直接アクセスを提供する「許容可能な」方法ですか?そのようなものをコーディングする他の方法はありますか?

4

1 に答える 1

2

ミーガーのコメントを見てください:

コードを醜く、維持しにくく、不便にしています...正確には何ですか? インターフェイスを定義します。それはあなたのクラスへのインターフェースです。ばかげたテンプレート フラグ ハッカーを使用して、開発者がそれをバイパスすることを許可しないでください。コードを書いている場合は、自分が何をしているのかを常に理解している必要があります。<true>自分が何をしているのかを特に知っていることを示すために明示的に入力する必要があるのは... 非常に、非常に頭がおかしいです。開発者にはドキュメントがあります。補助輪や人為的な制限は必要ありません。物事を成し遂げるための明確で簡潔なコードが必要です。– ミーガー 2012-10-06 02:41:53Z

他のユーザーに提供するクラスは、別のユーザーが使用したときに予期しない状態になることがあってはなりません。この場合の予期しない状態とは、クラスを作成するときに考慮したことのない状態です。その ため、クラスの低レベル メソッドへのアクセスを決して許可しないか、潜在的な欠陥を文書化する必要があります。

あなたがロガーを書いているとしましょう:

struct MyLogger{
    MyLogger(std::string filename) : stream(filename.c_str()){}      
    template <typename T>
    MyLogger& operator<<(const T& v){ stream << v << " "; return *this;}

private:
    std::ofstream stream;
};

コピー コンストラクターがなく、代入オペランドがないことは無視してください。また、タイムスタンプすら提供しない粗雑なロガーであることも無視してください。ただし、ご覧のとおり、ロガーの状態はロガーのメソッドに完全に依存します。たとえば、ファイルが正常に開かれた場合、ロガーが破棄されるまでファイルは閉じられません。

ここで、あなたのアプローチを使用するとします。

struct MyLogger{
    MyLogger(std::string filename) : stream(filename.c_str()){}
    template <typename T>
    MyLogger& operator<<(const T& v){ stream << v << " "; return *this;}

    template<bool RemoveProtection = false> 
    inline std::ofstream& writeStream()
    {
        static_assert(RemoveProtection, "You're doing it wrong");
        return stream;
    }

    inline const std::ofstream& writeStream() const
    {
        return stream;
    }

private:
    std::ofstream stream;
};

そして今、誰かが次のコードを使用しています

logger.writeStream<true>.close();

バン。ロガーが壊れています。もちろん、それはユーザーのせい<true>です。しかし、多くの場合、ユーザーはサンプル コードをコピーしようとします。特にライブラリを初めて使用する場合はそうです。ユーザーはあなたの例を見ます

logger.writeStream().good(); // <- OK
logger.writeStream().put('c'); // <- NOT OK
logger.writeStream<true>().put('c'); // <- OK

最初はドキュメントを完全に無視します。次に、最初と最後のバージョンを使用します。後で彼は、最後のバージョンが毎回機能することを発見しました! 何と奇跡的なことでしょう<true>。そして、彼は起こった悪いことについてあなたを責め始めたので、あなたは警告を含む文書で露骨な炎から身を守ることができました:

    /**
    * \brief Returns a reference to the internal write stream
    *
    * \note You have to use the template parameter `<true>` in order to use it
    *
    * \warning Please note that this will return a reference to the internal 
    *          write stream. As such you shouldn't create any state in which 
    *          the logger cannot work, such as closing the stream.        
    */
    template<bool RemoveProtection = false> 
    inline std::ofstream& writeStream()
    {
        static_assert(RemoveProtection, "You're doing it wrong");
        return stream;
    }

それで、私たちは何を手に入れましたか?その警告をどこかに置く必要がありました。stream公開した方がずっと簡単だったでしょう:

struct MyLogger{
    MyLogger(std::string filename) : stream(filename.c_str()){}
    template <typename T>
    MyLogger& operator<<(const T& v){ stream << v << " "; return *this;}

    /**
    * The internal write stream. Please look out that you don't create
    * any state in which the logger cannot work, such as closing the stream.
    */
    std::ofstream stream;
};

またはくっついた

/** >put warning here< */
inline std::ofstream & writeStream()
{
    return stream;
}

おっと。したがって、低レベルのメソッドへのアクセスを許可しない (特定のメソッドのstd::ofstream使用を許可する必要がある場合は、特定のメソッドのラッパーを作成する) か、オブジェクトの内部を大幅に変更した場合に発生する可能性のある欠陥を文書化しますが、変更しないでください。途中でstatic_assert.

于 2012-10-06T03:23:04.717 に答える