-1

debugオブジェクトに関する情報を出力する関数を作成したいと考えています。私のシステムには、さまざまな種類のオブジェクトが含まれています。それらのいくつかには、他のオブジェクトが含まれています。

using namespace std; // for brevity
struct dog {string name;};
struct human {string name; string address;};
struct line {list<human*> contents;};
struct pack {vector<dog*> contents;};

関数に引数のメンバーがある場合はそのメンバーを出力するnameか、引数のメンバーがある場合はそれをデバッグするcontentsようにします。次のコードを思いつきました:

template <class T>
void debug(T object) // T here is a simple object like dog, human, etc
{
    cout << object.name.c_str() << '\n';
}

// A helper function, not really important
template <class T>
void debug_pointer(T* object)
{
    debug(*object);
}

void debug(pack object)
{
    for_each(object.contents.begin(), object.contents.end(), debug_pointer<dog>);
}

void debug(line object)
{
    for_each(object.contents.begin(), object.contents.end(), debug_pointer<human>);
}

packここで、とのコードlineはほぼ同じです! 同じコードを何度も書くのは避けたい:

struct line {list<human*> contents; typedef human type;};
struct pack {vector<dog*> contents; typedef dog type;};

template <class T>
void debug(T object) // T here is a compound object (having contents)
{
    for_each(object.contents.begin(), object.contents.end(), debug_pointer<T::type>);
}

ただし、この構文は、「単純な」オブジェクト (同じ署名を持つ) の関数テンプレートと競合します。

コードを書き直すにはどうすればよいですか? 私のプログラムのその部分はすでに非常に複雑であり、デバッグのためだけに何か(基本クラス、メンバー関数など)を追加するのは場違いに思えますdoghuman

4

4 に答える 4

1

C++11decltypeと SFINAE を使用すると、作業が簡単になります :)

#include <string>
#include <vector>
#include <list>
#include <iostream>
#include <algorithm>

struct dog { std::string name; };
struct human { std::string name; std::string address; };
struct line { std::list<human*> contents; };
struct pack { std::vector<dog*> contents; };

template <typename T>
auto debug(T const& t) -> decltype(t.name, void(0)) {
  std::cout << t.name << '\n';
}

template <typename T>
auto debug(T const* t) -> decltype(t->name, void(0)) {
  if (t != 0) std::cout << t->name << '\n';
}

struct Debugger {
  template <typename T>
  void operator()(T const& t) { debug(t); }
};

template <typename C>
auto debug(C const& c) -> decltype(c.contents, void(0)) {
  typedef decltype(c.contents) contents_type;
  typedef typename contents_type::value_type type;
  std::for_each(c.contents.begin(), c.contents.end(), Debugger());
}


int main() {
  dog dog1 = { "dog1" }, dog2 = { "dog2" };
  human h1 = { "h1" }, h2 = { "h2" };

  line l; l.contents.push_back(&h1); l.contents.push_back(&h2);

  debug(l);

}

ideoneで実行すると、次の結果が得られます。

h1
h2

予想通り :)

C++11 を使用しない場合、ちょっとした工夫が必要ですが、原則は同じままです。boost::enable_ifと の存在とアクセシビリティに基づいてコンパイル エラーを引き起こす構造を作成する必要がありnameますcontents

もちろん、構造自体にメソッドを接続するだけで簡単になります:)

于 2012-01-29T16:46:23.953 に答える
1

コンテナーもテンプレート パラメーターにします。

template <template <typename> class Container, typename T>
void debug(Container<T> object)
{
    for_each(object.contents.begin(), object.contents.end(), debug_pointer<T>);
}

ところで、ほとんどの場合、値ではなく const 参照で渡したい場合があります (ベクター/リスト全体をコピーする必要があります)。

template <template <typename> class Container, typename T>
void debug(const Container<T>& object)

C++11 を使用できる場合は、コンテンツからdecltypeを判別するために使用できます。T

template <typename T>
void debug(const T& object)
{
    typedef decltype(*object.contents.front()) T;
    for_each(object.contents.begin(), object.contents.end(), debug_pointer<T>);
}

GCC にはtypeofC++11 が使用できない場合もあります。

于 2012-01-29T15:50:12.993 に答える
1

基本的なコードは次のようになります。

template <typename T> void debug(T const & x)
{
    debug_helper<T, has_name<T>::value>::print(x);
}

ヘルパー クラスが必要です。

template <typename, bool> struct debug_helper;

template <typename T> struct debug_helper<T, true>
{
    static void print(T const & x) { /* print x.name */ }
};
template <typename T> struct debug_helper<T, false>
{
    static void print(T const & x) { /* print x.content */ }
};

SFINAE trait classhas_name<T>と、コンテナを出力するメカニズムが必要です。これらの問題は両方とも、プリティ プリンター コードでほぼそのまま解決されます。

于 2012-01-29T16:17:47.550 に答える
0

SFINAE を使用して、使用されているオーバーロードを選択できます。

正確な詳細は忘れましたが、「コンテンツ」メンバーまたは「名前」メンバーの存在を検出し、それに基づいてオーバーロードするために使用できます。

于 2012-01-29T16:06:44.697 に答える