4

MS CStringのように動作するクラスを作成しようとしています(つまり、printfに渡すと、「。c_str()」のような醜い黒魔術を追加せずに、C文字列へのポインターのように動作します)。

これは、このクラスの最初の実装であり、機能するだけで、まだ有用なものは何も提供していません。

#include <cstdlib>
#include <cstring>

class CString
{
protected:
    struct CStringInfo
    {
        size_t Length;
        size_t MaxLength;
    };

public:
    CString()
    {
        Buffer = NULL;

        Assign(NULL);
    }

    CString(const char* chv)
    {
        Buffer = NULL;

        Assign(chv, 0);
    }

    ~CString()
    {
        if(Buffer) delete[] Buffer;
        Buffer = NULL;
    }

    size_t GetLength()
    {
        if(!Buffer) Alloc(1);
        return GetInfo()->Length;
    }

    size_t Resize(size_t size)
    {
        Alloc(size + 1); // + 0x00
        Buffer[size] = 0;
        return size;
    }

    bool Assign(const char* value, size_t size = 0)
    {
        size_t strl = ((size) ? size : strlen(value));

        if(!value || !(strl = strlen(value)))
        {
            if(!Buffer) Alloc(1);
            return false;
        }

        Alloc(strl + 1);
        memcpy(Buffer, value, strl);
        Buffer[strl] = 0;
        return true;
    }

    CString& operator = (const char* what)
    {
        Assign(what);
        return (*this);
    }

    CString& operator = (CString& string)
    {
        Assign(string.Buffer);
        return (*this);
    }

    operator const char* ()
    {
        return Buffer;
    }

protected:
    char* Buffer;

    void Alloc(size_t size)
    {
        if(!size) size = 1;
        char* nb = new char[size + sizeof(CStringInfo)];
        char* nbb = nb + sizeof(CStringInfo);
        size_t cl = size - 1;
        if(Buffer)
        {
            if(cl > GetInfo()->Length) cl = GetInfo()->Length;
            if(cl) memcpy(nbb, Buffer, cl - 1);
            nbb[cl] = 0;
            *(CStringInfo*)(nb) = *(CStringInfo*)(Buffer);
            delete[] (Buffer - sizeof(CStringInfo));
        }

        Buffer = nb;
        GetInfo()->MaxLength = size;
        GetInfo()->Length = cl;
    }

    void Free()
    {
        if(Buffer)
        {
            delete[] (Buffer - sizeof(CStringInfo));
        }
    }

    CStringInfo* GetInfo()
    {
        return (CStringInfo*)(this->Buffer - sizeof(CStringInfo));
    }
};

そして私がそれをテストするコード:

#include <cstdio>
#include "CString.hpp"

CString global_str = "global string!";

int main(int argc, char* argv[])
{
    CString str = "string";
    printf("Test: %s, %s\n", str, global_str);
    return 0;
}

クラスにデストラクタがない場合は、それをprintfに渡すことができ、正常に機能します(C文字列として)。しかし、デストラクタを追加すると、GCCは次のエラーを生成します。

error: cannot pass objects of non-trivially-copyable type 'class CString' through '...'

また、以前のバージョンのGCCに加えて、警告+ud2オペコードが表示されます。

だから...質問:私は実際にGCCで次の構築作業を行うことができますか、それとも上記のコードと同じものを使用するために、おそらくC varargsを含まない方法はありますか?

4

4 に答える 4

3

キャストを使用して変換演算子をトリガーできます。

printf("Test: %s, %s\n", static_cast<const char*>(str), 
       static_cast<const char*>(global_str));

ただし、これで問題が発生するかどうかはわかりません。C++コードでvarargsを回避するのがおそらく最善でしょう。

代わりにタイプセーフなprintfを使用するのはどうですか(クレジット:ウィキペディア):

void printf(const char *s)
{
    while (*s) {
        if (*s == '%') {
            if (*(s + 1) == '%') {
                ++s;
            }
            else {
                throw std::runtime_error("invalid format string: missing arguments");
            }
        }
        std::cout << *s++;
    }
}

template<typename T, typename... Args>
void printf(const char *s, T value, Args... args)
{
    while (*s) {
        if (*s == '%') {
            if (*(s + 1) == '%') {
                ++s;
            }
            else {
                std::cout << value;
                printf(s + 1, args...); // call even when *s == 0 to detect extra arguments
                return;
            }
        }
        std::cout << *s++;
    }
    throw std::logic_error("extra arguments provided to printf");
}

std::runtime_errorlibstdc++はサポートしていないと思いstd::logic_errorます。

于 2013-01-21T04:38:29.857 に答える
3

foo.c_str()直接( )またはキャスト()のようなものを介して、メンバー関数を呼び出す必要があります(char *)foo

それ以外の場合は、コンパイラによって異なります。C ++ 03では、動作は未定義です(§5.2.2/ 7):

特定の引数にパラメーターがない場合、受信関数がva_arg(18.7)を呼び出すことによって引数の値を取得できるように、引数が渡されます。左辺値から右辺値(4.1)、配列からポインター(4.2)、および関数からポインター(4.3)の標準変換は、引数式で実行されます。これらの変換後、引数に算術、列挙、ポインター、メンバーへのポインター、またはクラスタイプがない場合、プログラムの形式は正しくありません。引数が非PODクラスタイプ(9節)の場合、動作は未定義です。

...しかし(C ++ 11、§5.2.2/ 7)では、条件付きでサポートされています:

特定の引数にパラメーターがない場合、引数は、左辺値から右辺値(4.1)、配列からポインター(4.2)、および関数からポインター(4.3)の標準を受け取るように渡されます。変換は引数式で実行されます。(おそらくcv修飾された)タイプstd :: nullptr_tを持つ引数は、タイプvoid *(4.10)に変換されます。これらの変換後、引数に算術、列挙、ポインター、メンバーへのポインター、またはクラスタイプがない場合、プログラムの形式は正しくありません。自明でないコピーコンストラクター、自明でない移動コンストラクター、または自明でないデストラクタを持ち、対応するパラメーターがないクラスタイプ(条項9)の潜在的に評価される引数を渡すことは、実装定義のセマンティクスで条件付きでサポートされます。

「実装定義のセマンティクスで条件付きでサポート」は、適切なドキュメントで実装をサポートするための開口部を残しますが、それでも、可能な限り未定義の動作に近いものです。

これを行う場合は、可変個引数テンプレートを使用して、ある種の仲介者を設定すると思います。foo.c_str()これにより、タイプの引数を渡したprintfときに(たとえば)自動的に渡されるオーバーロードを提供しますstd::string。それは(おそらく)より多くのコードですが、少なくとも実際には機能します。個人的には、それが価値があるよりも単に厄介であるとして、私はすべてを避けたいと思います。

于 2013-01-21T04:45:02.037 に答える
0

この(醜い、正直なところ)文字列クラスを書くことで何を解決しようとしていますか?他にsmthを使用しないのはなぜですか?(のようにstd::string)-あなた自身の超最適化された文字列を書き始める前によく考えてください...

あなたの質問について:あなたはあなたのサンプルコードで本当に幸運です!Cで楕円が(マシンコードで)どのように機能するのか、そしてなぜそれを介して自明でない型を渡すことが許可されないのか、あなたは何か考えがありますか?簡単に言うprintf()と、フォーマット文字列を調べるだけで、その中に'%s'が含まれている場合は、次の引数がchar*すべてであると見なされます。したがって、代わりに他のもの(、など)を渡すcharshort、UBになります。sizeof()(予想と異なる場合は、すぐにセグメンテーション違反が発生する可能性が高くなります。..C ++では楕円が悪い習慣になっているのはそのためです!完全にタイプセーフではありません!

C ++を使用している場合は、CAPIを使用しないでください。出力をフォーマットするために設計されたC++ライブラリがたくさんあり(boost :: formatなど)、タイプセーフです!C ++ 11は、printfのような機能の扉を開きますが、型安全性が保証されています。可変個引数テンプレートに関する「古典的な」例を読んでください...そして読んだ後でのみ、独自の文字列を実装してみてください:)

于 2013-01-21T04:48:13.190 に答える
0

varargsを介してオブジェクトを渡すことはできず、オブジェクトへのポインタのみを渡すことができます。ただし、 C ++形式printfで提供されるような(可変個引数の)テンプレートベースの実装を使用できます。

#include "format.h"
#include "CString.hpp"

CString global_str = "global string!";

std::ostream &operator<<(std::ostream &os, const CString &s) {
  return os << static_cast<const char*>(s);
}

int main() {
  CString str = "string";
  fmt::printf("Test: %s, %s\n", str, global_str);
}

これにより、「テスト:文字列、グローバル文字列!」が出力されます。CString正しく実装されている場合。

Jesse Goodの実装とは異なり、これは標準のprintfフォーマット指定子をサポートします。

免責事項:私はこのライブラリの作者です

于 2016-02-12T15:14:47.917 に答える