1

カスタム エラー クラスを作成しようとしています。そのコンストラクターは、引数を に渡すことによってエラー メッセージを作成しますfmt::format()FMT_STRING()スローするたびに明示的に使用しなくても、引数に対してフォーマット文字列を常にコンパイル時にチェックすることをお勧めします。何かのようなもの:

class Err : public std::exception 
{
private:
    std::string m_text;
public: 
    template <typename S, typename... Args>
    Err(const S& format, Args&&... args) {
        m_text = fmt::format(FMT_STRING(format), args...);
    }
    
    virtual const char* what() const noexcept {return m_text.c_str();}
};

// ------------------------ 

throw Err("Error {:d}", 10);     // works
throw Err("Error {:d}", "abc");  // cause Compile-time error

上記のコードでは、FMT_STRING() マクロでエラーが発生します。

error C2326: 'Err::{ctor}::<lambda_1>::()::FMT_COMPILE_STRING::operator fmt::v7::basic_string_view<char>(void) const': function cannot access 'format' 
message : see reference to function template instantiation 'Err::Err<char[11],int>(const S (&),int &&)' being compiled with [ S=char [11] ]

テンプレート プログラミングの経験はほとんどありません。FMT_STRING()毎回明示的に使用せずに、これを常にコンパイル時にフォーマット文字列をチェックさせるにはどうすればよいですか?

4

2 に答える 2

2

今日、まったく同じ問題に直面しましたが、{fmt} ライブラリはその後改善されたと思います。これが私の解決策です。

ここでの考え方は、例外コンストラクターで fmt::format 呼び出しとまったく同じ呼び出しを行うことです。 fmt::format_string オブジェクトの構築は、C++20 consteval を使用して、コンパイル時にフォーマット文字列を解析およびチェックします。次に、フォーマット文字列と可変引数が vformat に渡され、vformat が実際の作業を行います。

#include <fmt/format.h>
#include <exception>
#include <iostream>

class Err : public std::exception 
{
private:
    std::string m_text;
public: 
    template <typename... Args>
    Err(fmt::format_string<Args...> fmt, Args&&... args) {
        m_text = fmt::vformat(fmt, fmt::make_format_args(args...));
    }
    
    virtual const char* what() const noexcept {return m_text.c_str();}
};

int main() {
    try {
        throw Err("Error {:d}", 10); // Works
        throw Err("Error {:d}", "abc"); // Compile error
    }
    catch(const std::exception& e){
        std::cout << e.what() << std::endl;
    }
}

私のバージョンでは、単一の文字列を取り、書式設定を完全にスキップするコンストラクターのオーバーライドを提供し、書式設定コンストラクターを次のように変更することになりました。

Err(fmt::format_string<T, Args...> fmt, T&& p1, Args&&... args) {
    m_text = fmt::vformat(fmt, fmt::make_format_args(p1, args...));
}

これは、パラメーターが 1 つしかない場合に、他のオーバーロードが常に選択されるようにするためです。

于 2021-11-18T21:34:12.137 に答える