14

C ++は私の第一言語ではないので、助けていただければ幸いです。

複数のライブラリで派生したテンプレートクラスがあります。各派生クラスにidintを一意に割り当てる方法を見つけようとしています。ただし、静的メソッドからそれを実行できる必要があります。


template < class DERIVED >
class Foo
{
public:
    static int s_id()
    {
        // return id unique for DERIVED
    }
    // ...
};
ありがとうございました!

4

11 に答える 11

11

これはごくわずかなコードで実行できます。

template < class DERIVED >
class Foo
{
public:
    static int s_id()
    {
        return reinterpret_cast<int>(&s_id);
    }
};
于 2011-10-07T19:44:03.710 に答える
9

最新のC++(03-gccなどの最近のコンパイラを使用していると仮定)では、typeidキーワードを使用して、少なくとも実行時に基本的な型情報を提供するtype_infoオブジェクトを取得できます。これは標準(およびクロスプラットフォーム)機能です。

ウィキペディアから例を取り、テンプレート/継承チェックを追加しました。これはうまく機能しているようですが、intバージョンについてはわかりません(これは、コンパイラが読み取り専用のどこかに型名を持っているという仮定を悪用するハックですメモリスペース...それは間違った仮定かもしれません)。

文字列識別子は、あなたの場合にそれを使用できるのであれば、クロスプラットフォームの識別にははるかに優れているようです。コメントで示唆されているように、名前は標準で「実装定義」されているため、クロスコンパイラとの互換性はありません。

完全なテストアプリケーションコード:

#include <iostream>
#include <typeinfo>  //for 'typeid' to work

class Person 
{
public:
   // ... Person members ...
   virtual ~Person() {}
};

class Employee : public Person 
{
   // ... Employee members ...
};

template< typename DERIVED >
class Test
{
public:
    static int s_id()
    {
        // return id unique for DERIVED
        // NOT SURE IT WILL BE REALLY UNIQUE FOR EACH CLASS!!
        static const int id = reinterpret_cast<int>(typeid( DERIVED ).name());
        return id;
    }

    static const char* s_name()
    {
        // return id unique for DERIVED
        // ALWAYS VALID BUT STRING, NOT INT - BUT VALID AND CROSS-PLATFORM/CROSS-VERSION COMPATBLE
        // AS FAR AS YOU KEEP THE CLASS NAME
        return typeid( DERIVED ).name();
    }
};

int wmain () 
{
    Person person;
    Employee employee;
    Person *ptr = &employee;



    std::cout << typeid(person).name() << std::endl;   // Person (statically known at compile-time)
    std::cout << typeid(employee).name() << std::endl; // Employee (statically known at compile-time)
    std::cout << typeid(ptr).name() << std::endl;      // Person * (statically known at compile-time)
    std::cout << typeid(*ptr).name() << std::endl;     // Employee (looked up dynamically at run-time
                                                    // because it is the dereference of a pointer to a polymorphic class)

    Test<int> test;
    std::cout << typeid(test).name() << std::endl;    
    std::cout << test.s_id() << std::endl;    
    std::cout << test.s_id() << std::endl;    
    std::cout << test.s_id() << std::endl;    
    std::cout << test.s_name() << std::endl;    

    Test< Person > test_person;
    std::cout << test_person.s_name() << std::endl;    
    std::cout << test_person.s_id() << std::endl;    

    Test< Employee > test_employee;
    std::cout << test_employee.s_name() << std::endl;    
    std::cout << test_employee.s_id() << std::endl;    

    Test< float > test_float;
    std::cout << test_float.s_name() << std::endl;    
    std::cout << test_float.s_id() << std::endl;    


    std::cin.ignore();
    return 0;
}

出力:

class Person
class Employee
class Person *
class Employee
class Test<int>
3462688
3462688
3462688
int
class Person
3421584
class Employee
3462504
float
3462872

これは少なくともVC10Beta1とVC9で機能し、GCCで機能するはずです。ちなみに、typeid(およびdynamic_cast)を使用するには、コンパイラで実行時型情報を許可する必要があります。デフォルトでオンになっているはずです。一部のプレートフォーム/コンパイラ(一部の組み込みハードウェアについて考えています)では、RTTIはコストがかかるためオンになりません。したがって、極端な場合には、より良い解決策を見つける必要があります。

于 2009-05-28T22:26:42.893 に答える
2

以前の会社では、クラス名をパラメーターとして受け取るマクロを作成し、(クラス名に基づいて)一意のIDを使用してローカル静的を作成し、基本クラスで宣言された仮想関数のオーバーライドを作成することでこれを行いました。静的メンバーを返しました。このようにして、Javaオブジェクトの「getClass()」メソッドと同様に、オブジェクト階層の任意のインスタンスから実行時にIDを取得できますが、はるかに原始的です。

于 2009-05-28T18:41:47.667 に答える
2

私はこれまでのところ答えに100%満足しているわけではなく、私のために1つの解決策を考え出しました。アイデアは、typeinfoを使用してタイプ名のハッシュを計算することです。アプリケーションのロード時にすべてが1回実行されるため、実行時の過負荷は発生しません。このソリューションは、タイプ名として共有ライブラリを使用しても機能し、そのハッシュは一貫しています。

これは私が使用するコードです。これは私にとって素晴らしい働きをします。

#include <string>
#include <typeinfo>
#include <stdint.h>

//###########################################################################
// Hash
//###########################################################################
#if __SIZEOF_POINTER__==8
inline uint64_t hash(const char *data, uint64_t len) {
    uint64_t result = 14695981039346656037ul;
    for (uint64_t index = 0; index < len; ++index)
    {
        result ^= (uint64_t)data[index];
        result *= 1099511628211ul;
    }
    return result;
}
#else
inline uint32_t hash(const char *data, uint32_t len) {
    uint32_t result = 2166136261u;
    for (uint32_t index = 0; index < len; ++index)
    {
        result ^= (uint32_t)data[index];
        result *= 16777619u;
    }
    return result;
}
#endif
inline size_t hash(const std::string & str) { return hash(str.c_str(), str.length()); }

//###########################################################################
// TypeId
//###########################################################################
typedef size_t TypeId;

template<typename T>
static const std::string & typeName() {
    static const std::string tName( typeid(T).name() );
    return tName;
}
template<typename T>
static TypeId typeId() {
    static const TypeId tId = hash( typeName<T>() );
    return tId;
}
于 2016-01-14T09:28:38.813 に答える
2
#include <stdint.h>
#include <stdio.h>

#define DEFINE_CLASS(class_name) \
    class class_name { \
    public: \
        virtual uint32_t getID() { return hash(#class_name); } \

// djb2 hashing algorithm
uint32_t hash(const char *str)
{
    unsigned long hash = 5381;
    int c;

    while ((c = *str++))
        hash = ((hash << 5) + hash) + c; /* hash * 33 + c */

    return hash;
}

DEFINE_CLASS(parentClass)

    parentClass() {};
    ~parentClass() {};
};

DEFINE_CLASS(derivedClass : public parentClass)

    derivedClass() : parentClass() {};
    ~derivedClass() {};
};

int main() {
    parentClass parent;
    derivedClass derived;
    printf("parent id: %x\nderived id: %x\n", parent.getID(), derived.getID());
}
于 2018-01-10T17:06:47.420 に答える
1

どんなID?原子的に増加するintを探していますか?文字列に問題がない場合はどうでしょうか。

static string s_id()
{
   return typeid(Foo<DERIVED>).name();
}

intである必要があるが、自動的に増加しない場合は、衝突が発生する可能性が低い128ビット整数のハッシュを作成できます(ただし、必要以上の数になる可能性があります)。

于 2009-05-28T20:45:16.983 に答える
1

これが私がやったことです。フィードバック(長所、短所)があれば、私に知らせてください。


template < class DERIVED >
class Foo
{
public:
    static const char* name(); // Derived classes will implement, simply
// returning their class name static int s_id() { static const int id = Id_factory::get_instance()->get_id(name()); return id; } // ... };

基本的に、IDは、ポインター比較ではなく文字列比較を行った後に割り当てられます。これは速度の点では理想的ではありませんが、idをstatic constにしたので、DERIVEDごとに1回だけ計算する必要があります。

于 2009-05-29T19:04:34.983 に答える
1

以下のスニペットは、VS(2015)およびリリースビルドで機能します。

template <typename T>
struct TypeId
{
  static size_t Get()
  {
    return reinterpret_cast<size_t>(&sDummy);
  }

private:
  static char sDummy;
};

template <typename T>
char TypeId<T>::sDummy; // don't care about value

また、GCC v7.3(Ubuntu 16.04)およびLLVM v10.0.0(Mac OS High Sierra)でも試してテストしました。

仕組み:TypeId<>テンプレートの各インスタンス化は、独自の一意のアドレスを持つ独自の一意のsDummyインスタンスを取得します。正直なところ、関数静的バージョンがリリースで機能しなかった理由は完全にはわかりません。同じコマンドの折りたたみと最適化が疑われます。

読者のための演習:少なくともconst型とref型は、raw型と同じ型IDを取得する必要があります。

于 2019-03-31T06:13:05.857 に答える
0

標準化されたものはありません。さらに、絶対確実なハックはありません。

私が思いついた最高のもの:

template < class DERIVED, int sid >
class Foo
{
    public:    
      static int s_id()    
      {        
          return sid;
      }    
};

Foo<MyClass, 123456>   derivedObject;
于 2009-05-28T18:35:14.717 に答える
0

次のことができます。

#include <iostream>


template <int id = 5>
class blah
{
public:
    static const int cid = id; 
};

int main(int argc, char *argv[])
{
    std::cout << blah<>::cid << " " << blah<10>::cid << std::endl;

}

でもそれがいい考えかどうかはわかりません。そのblah<>部分も少し直感的ではありません。手動でIDを割り当てる方がよい場合もあれば、基本型を作成して、クラスIDを使用してテンプレート引数を指定することもできます。

于 2009-05-28T18:38:06.973 に答える
0

Paul Houxがここで指摘したように、静的メソッドのアドレスを返すトリックは、コンパイラの最適化のために機能しない場合があります。ただし、マクロ+キーワードを使用して回避策を作成__FILE__でき__LINE__ますvolatile

最終的な解決策は次のようになります。

#define GET_TYPE_ID static size_t GetTypeId()   \
{                                               \
    volatile const char* file = __FILE__;       \
    volatile uint32_t line = __LINE__;          \
    return (size_t)&GetTypeId;                  \
}

class ClassA
{
public:
    GET_TYPE_ID
};

class ClassB
{
public:
    GET_TYPE_ID
};
于 2020-07-16T14:48:04.143 に答える