:
- あなたの質問はan XY problemで、あなただけのリストを知っている様々なタイプのベクトルに格納し、ポインタが解決策ではなく、解決策になるかもしれないと思う、と
コンパイル時にコンポーネントタイプを指定すると、これは比較的簡単ですそれはタプルとそれを反復する何らかの仕組みによって実現されます。
まず、我々は、コンポーネントタイプのリストを指定して、適切なタプル型を製造するためのメタ関数をしたい:
namespace detail {
template<std::size_t N, typename... Components>
std::tuple<std::array<Components, N>...>
makeComponentPool(std::tuple<Components...>) noexcept;
} // namespace detail
template<std::size_t N, typename ComponentTup>
using ComponentPool = decltype(detail::makeComponentPool<N>(std::declval<ComponentTup>()));
// example:
static_assert(std::is_same<
ComponentPool<10, std::tuple<AIComponent, PhysicsComponent, RenderComponent>>,
std::tuple<
std::array<AIComponent, 10>,
std::array<PhysicsComponent, 10>,
std::array<RenderComponent, 10>
>
>::value);
はその後、我々は、タプルを反復処理のいくつかの方法が必要です。 boost::fusion::for_each
はここでうまく機能、または私たちは私たち自身をロールバックすることができます:
namespace detail {
template<typename TupT, typename FunT, std::size_t... Is>
void for_each(TupT&& tup, FunT&& f, std::index_sequence<Is...>) {
using expand = int[];
(void)expand{0, (f(std::get<Is>(std::forward<TupT>(tup))), void(), 0)...};
}
} // namespace detail
template<
typename TupT, typename FunT,
std::size_t TupSize = std::tuple_size<std::decay_t<TupT>>::value
>
void for_each(TupT&& tup, FunT&& f) {
detail::for_each(
std::forward<TupT>(tup), std::forward<FunT>(f),
std::make_index_sequence<TupSize>{}
);
}
は、今、私たちは、意思決定を行う必要があります。各コンポーネントタイプが同じ公衆アクセスポイントを持っている必要がありますか?質問には、update()
とrender()
の両方があります。私たちはこれらすべてに同じ名前を付けることができしかし場合(例えばprocess()
)、その後、物事は非常に簡単です:
struct AIComponent { void process() { } };
struct PhysicsComponent { void process() { } };
struct RenderComponent { void process() { } };
class Game {
using ComponentTypes = std::tuple<AIComponent, PhysicsComponent, RenderComponent>;
static constexpr std::size_t MAX_NUM = 3;
ComponentPool<MAX_NUM, ComponentTypes> componentPool;
std::atomic_bool gameOver{false};
public:
void runGame() {
while (!gameOver) {
for_each(componentPool, [](auto& components) {
for (auto& component : components) {
component.process();
}
});
}
}
void endGame() { gameOver = true; }
};
Online Demo
(N.b.デモでprocess()
は、任意の実装要件に起因するだけでなく、博覧会のためのパラメータを、与えられた。)
は今、あなただけのMAX_NUM
とComponentTypes
を管理するために必要な他のすべての場所に落ちます。あなたが別のコンポーネントタイプごとに異なるアクセス・ポイント(問題のようRenderComponent
ためAIComponent
とPhysicsComponent
しかしrender()
ため、例えばupdate()
、)を可能にする場合は
しかし、我々は明らかに仕事のビットを行うには残っています。 1つのアプローチは、アクセスポイントを呼び出すためのレベルのインダイレクションを追加することです。最小限のオーバーヘッド(構文と実行時の両方)できちんと実行する方法の1つは、コンポーネント処理を自明に行うために、 - タイプ別。 overload
の必要に応じて、より堅牢な実装がオンラインで見つけることができる場合
template<typename FunT, typename... FunTs>
struct overloaded : private FunT, private overloaded<FunTs...> {
overloaded() = default;
template<typename FunU, typename... FunUs>
overloaded(FunU&& f, FunUs&&... fs)
: FunT(std::forward<FunU>(f)),
overloaded<FunTs...>(std::forward<FunUs>(fs)...)
{ }
using FunT::operator();
using overloaded<FunTs...>::operator();
};
template<typename FunT>
struct overloaded<FunT> : private FunT {
overloaded() = default;
template<typename FunU>
overloaded(FunU&& f) : FunT(std::forward<FunU>(f)) { }
using FunT::operator();
};
template<typename... FunTs>
overloaded<std::decay_t<FunTs>...> overload(FunTs&&... fs) {
return {std::forward<FunTs>(fs)...};
}
、それでもこの単純な実装で、私たちは今、できること:ここではなく、関数ポインタのすべてのファンクタ(INC。ラムダ)のために働く基本的な実装であります次の手順を実行します。今すぐ
struct AIComponent { void update() { } };
struct PhysicsComponent { void update() { } };
struct RenderComponent { void render() { } };
class Game {
using ComponentTypes = std::tuple<AIComponent, PhysicsComponent, RenderComponent>;
static constexpr std::size_t MAX_NUM = 3;
ComponentPool<MAX_NUM, ComponentTypes> componentPool;
std::atomic_bool gameOver{false};
public:
void runGame() {
// `auto` overload is the least specialized so `update()` is the default
static auto process = overload(
[]( auto& comp) { comp.update(); },
[](RenderComponent& comp) { comp.render(); }
);
while (!gameOver) {
for_each(componentPool, [](auto& components) {
std::for_each(begin(components), end(components), process);
// alternatively, equivalently:
//for (auto& component : components) {
// process(component);
//}
});
}
}
void endGame() { gameOver = true; }
};
Online Demo
あなたが必要MAX_NUM
とComponentTypes
を管理し、process
に新しいオーバーロードを追加することもできます(ただし、忘れた場合はコンパイラエラーが発生します)。
今読んでいるのと同じC++の本を読んでください。ある時点で、継承とサブクラス化について十分に学習し、自分でこれを把握することができます。これは広範なテーマであり、stackoverflow.comの簡単な回答では完全にはカバーできません。 –
あなたが何を意味するか分かりませんが、多態性を使うことはできません。なぜなら、私はキャッシュフレンドリにしようとする目的を打ち破るポインタのベクトルを持たなければならないからです。 – Alex
このような種類のエキゾチックな環境については、すべてのフェムト秒が価値があると言われていませんでした。その場合、要件を満たす完全にカスタムのコンテナを作成する必要があります。これはまだあなたのC++の本でカバーされていますが、はるか後の、高度な章でカバーされます。間違いなくstack overflow.comの一般的な答えに絞り込むことはできません。標準のC++ライブラリコンテナは要件を満たしません。 –