2

これは、「コードが機能せず、その理由がわかりません」という別の質問です。std::map::insert が例外をスローする理由を知るには、stl に関する十分な知識がありません。どのような場合に例外がスローされるかがわかっている場合は、おそらくこのテキストの壁をスキップして、答えることができます。問題の背景がどうしても必要な場合は、それを試してください。私のコードを投稿し、何が行われたかを説明します。 stl についてよりよく知っているすべての人が、挿入の呼び出しで何が問題なのかを説明できれば、非常に感謝しています。

go to factory オブジェクトとして時折使用するオブジェクトを少し前に書きました。その主な目的は、基本的に文字列を取得し、文字列と「新しいオブジェクト関数の作成」ポインターの両方を格納することです。これにより、最終的に関数を呼び出し、文字列を渡し、有効な登録がある場合は、派生オブジェクトの新しいインスタンスを返します。話が減り、コードが増えました。ここに私が得たものがあります:

factory.h

#ifndef FACTORY_H
#define FACTORY_H

// library tools
#include <map>
#include <string>

// Simplified registration macros
#define DECLARE_DERIVED(T, base)    static Factory<base>::DerivedRegister<T> reg;
#define DEFINE_DERIVED(T, base, s)  Factory<base>::DerivedRegister<T> T::reg(s); 

template<class base>
class Factory
{
protected:
    template<class T>
    static base * createT() { return new T;}

public:
    typedef std::map<std::string, base*(*)()> map_type;

    virtual ~Factory(){ }

    static base * createInstance(const std::string & s)
    {
        if(!m_Map.count(s))
            return nullptr;
        std::map<std::string, base*(*)()>::iterator it = m_Map.find(s);
        return it->second();
    }

    template <class T>
    struct DerivedRegister;

protected:
    static map_type m_Map; 
};

template<class base>
template<class T>
struct Factory<base>::DerivedRegister : public Factory<base>
{
    DerivedRegister(std::string const & s)
    {
        m_Map.insert(std::pair<std::string, base*(*)()>(s, &createT<T>));
    }
};


#endif

here は、それが本当に迅速に行うことのより良い説明です。基本クラス class A があるとします。そして、任意の数の派生クラスがあります。A にテンプレート化されたファクトリ オブジェクトをどこかに作成し、派生レジスタ オブジェクトを手動で作成するか、派生クラス宣言内の上部にあるマクロを使用して、静的レジストリ オブジェクトを作成します。次に、実装でそれを定義し、そのコンストラクターを呼び出して、オブジェクトの識別に使用する文字列を渡します。ファクトリ メンバーの createInstance を使用すると、文字列識別子を渡すことができ、A * が指す派生オブジェクトを返すことができます。

例:

ああ

class A
{

};

A.cpp

// the map for this factory template has to be defined somewhere, as it is static
Factory<A>::map_type Factory<A>::m_Map;

bh

#include <A.h>
class B : public A
{
  // anywhere in declaration of derived B
  DECLARE_DERIVED(A, B)
};

b.cpp

 // just somewhere in cpp file
 DEFINE_DERIVED(A, B, "B")

main.cpp

int main()
{
  A * ptr;
  Factory<A> factory;

  ptr = factory.createInstance("B");
}

このオブジェクトは、過去にほとんど問題なく機能していました。今、私はもう少し複雑なプロジェクトを行っています。私はゲーム エンジンに関連するデータ編成/API 設計が好きで、(インスタンス化されていない) シェーダーをカタログ化するソリューションを実装しようとしています。プログラミングされていますが、必要な場合を除き、実行時にインスタンス化されません。それはさておき、この質問は実際にはd3d1​​1とは何の関係もありません。少なくとも私はそうではないことを願っています.

これが何が起こっているかです。グラフィック シェーダーの抽象クラスを表すオブジェクトがあります。作成するすべてのシェーダーは、このオブジェクトから派生する必要があります。その機能から派生させて実装する方法は、すべてのシェーダーごとに異なります。

名前空間同期でベース オブジェクト「SYNC::D3D11Shader」を呼び出し、派生シェーダー「ColorShader」「LightShader」および「TextureShader」を呼び出しましょう。レンダリング オブジェクト内でこれらのシェーダーのインスタンスの std::map を単純に作成したくないので、このようにレンダリング オブジェクト内にファクトリを作成します。

D3D11Renderer.h

class D3D11Renderer
{
    // many other members...
    Factory<D3D11Shader> m_ShaderFactory;
    // many other member...
};

D3D11Renderer.cpp

// define this templated classes map or you'll get undefined errors
Factory<SYNC::D3D11Shader>::map_type Factory<SYNC::D3D11Shader>::m_Map;

そして、ColorShader でマクロを次のように使用します。

D3D11ColorShader.h

class D3D11ColorShader : public SYNC::D3D11Shader
{
  // ...lotsa members
  DECLARE_DERIVED(D3D11ColorShader, SYNC::D3D11Shader)
  // lotsa member...
};

D3D11ColorShader.cpp

// define the registery object with it's key here
DEFINE_DERIVED(D3D11ColorShader, SYNC::D3D11Shader, "ColorShader")

これはすべて正常にコンパイルされ、例外がスローされる場所は、特に挿入呼び出しで、D3D11ColorShader.cpp の registryObjects コンストラクターを最初に呼び出す場所です。例外エラーは次のとおりです。

Syncopate.exe の 0x772315de で未処理の例外: 0xC0000005: アクセス違反の読み取り場所 0x00000004。

したがって、実際には、問題は、いつ std::map::insert が例外をスローするのか、そしてその理由に要約されます。私がやっていることについて、誰もが何らかの背景を尋ねてくるだろうということはわかっていました。低く見よ、テキストの巨大な壁が現れた!私が本当に必要としているのは直感だけです。

また、質問は実際には関係ないので、d3d11にタグを付ける必要がありますか?

4

3 に答える 3

2

ここ問題があります:

   std::map<std::string, base*(*)()>::iterator it = m_Map.find(s);
   return it->second();

への呼び出しがfind失敗した場合 (つまり、マップ内に「s」が見つからない場合)、 が返されm_Map.end()ます。それはノーノーである逆参照。

于 2012-09-27T02:37:09.407 に答える
1

私の推測では、これは静的変数の初期化の順序によるものです。この順序を制御する方法はありません。したがって、初期化が保証されているわけではありません。

Factory<A>::map_type Factory<A>::m_Map;

この初期化の前に呼び出されます:

DEFINE_DERIVED(A, B, "B")

この場合、後者のステートメントを最初に初期化する必要があるため、マップは割り当てられていません。

別の設計パターンでは、シングルトン ファクトリの初期化を制御します。ファクトリ オブジェクトを作成する明示的な Initialize 関数がそれぞれにある場合は、メインの開始時にこれを呼び出すことができます。例えば

Factory.h

class Factory {
private:
    static Factory* instance_;
public:
    static Initialize(){instance_=new Factory;}
    Factory* instance(){return instance_;}
}

工場.cpp

static Factory* Factory::instance_ = NULL;

多くのファクトリがある場合、それらすべてを初期化する単一の初期化関数が必要になる可能性があり、新しいファクトリを作成するときに忘れずに追加する必要があります。

于 2012-09-27T02:36:07.503 に答える
1

さて、私は実際にこのエラーに約 1 日取り組んできましたが、今になってようやく何が問題なのかわかりました。

問題 1:

派生シェーダー ヘッダーは、実際にはプロジェクト全体のどこにも含まれていませんでした。また、直接インスタンス化する必要がないという事実にもかかわらず、リンクしてビルドに含めることができるように、どこかに含める必要があります。

問題 2:

興味深いことに、コンビナトリアルが言ったように、初期化の順序は次々に行われませんでしたが、古いコードを見てみると、以前は正しく初期化されていたようです。ここでの違いは何ですか。派生オブジェクトのファクトリを別のオブジェクト内に配置し、次に基本クラスに配置しました。私が行っていたのは、基本クラス内で静的関数と静的ファクトリを宣言して、登録されている派生クラスを基本クラス自体からインスタンス化できるようにすることでした。代わりにファクトリが基本クラスに含まれ、インスタンス化が静的関数を介して行われる場合、すべての静的の初期化順序は一貫して正しいように見えます (これが常に正しいかどうかはわかりません)。これを変更した後、現在は正常に動作します。

だから今、私の答えは、実際にはプロジェクトのどこにも含まれていなかったオブジェクトへの参照を使用しようとすると、このようなオペレーティング システムの例外を取得することができます。このオブジェクトが含まれていないにもかかわらず、コンパイルが正常に行われたように見える理由を説明するコンパイラまたはリンカーについての十分な知識がありません。誰かが私の答えを拡張したい場合は、お願いします。

この苦境に関係する場合は、MSVC++ 2010 Express を使用します。

于 2012-09-27T03:24:36.963 に答える