gpt4 book ai didi

c++ - std::function 检查类的继承

转载 作者:行者123 更新时间:2023-11-30 05:15:16 25 4
gpt4 key购买 nike

如何附上std::is_base_ofstd::function实际上?

我认为这是不可能的,因为类型被删除了。

如何解决?我想缓存 std::function f=check whether X derived from B .
稍后将调用它。

#include <iostream>
#include <type_traits>
#include <functional>
using namespace std;
class B{ };
class C: B{};
int main(){
std::cout<<std::is_base_of<B,C>()<<std::endl; //print 1
std::function<bool()> f=[](X)->bool{ //<--- syntax error (X is class name)
return std::is_base_of<B,X>();
};
std::cout<<f(C)<<std::endl; //<--- possible? (should print 1)
return 0;
}

在实际情况中,f在代码的遥远位置调用,我不想实例化 BC .
(因此,我不能使用 dynamic_cast 来检查。)

(编辑)潜在问题:-

我正在尝试获取一个二维 bool 表 ( result # ) 以进行一些复杂的类型操作。

class B{};
class C : B{};
class D{};
int main(){

for(auto cachefunction : cacheFunctions){
cachefunction();
//^ "cacheFunctions" is {register<B>(), register<C>(), register<D>()}
}
//in real case, cacheFunctions.size() ~ 200+
auto result=.... //#
}

可以编辑 register<T>() 中的代码随心所欲,但我不能要求用户调用register<T1,T2>对于每个可能的元组。

粗略地说,result #bool 的数组标记是否T2源自 T1 .

       B  C  D  (parent)
------------
(derived)
B x 0 0
C 1 x 0
D 0 0 x (x=don't care, 0 or 1 are OK)

int[]/std::vector<int> result = {x,0,0 , 1,x,0 , 0,0,x} .

我的最终目标是在 //# 行获取表格.

(编辑)潜在问题第二次尝试:-

我有一个这样调用我的库的用户代码。
这些线分散在许多用户的周围 .cpp :-

requestSystem<S1>()->someBFunction();
requestSystem<S2>()->someCFunction();
....

Si是我库中的子系统(在这里,我使用组合而不是继承。)。

到目前为止,我已经成功地使用了一些技巧 (array of std::function) 来实例化那些子系统 (new Si()),然后才在运行时实际调用这些函数。因此,它运行良好。

随着我的程序的增长,更多Si出生。
我注意到有些情况下 Sy最好继承自某个Sx .

enter image description here

在这种情况下,我发现如果我同时实例化 new Sx()new Sy() ,我的程序会表现得很奇怪,因为有两个基类实例 Sx (它应该是单例设计)。

我认为如果我可以通过在 requestSystem<T>() 中嵌入一些额外的代码来自动检测此类情况会很好仅实例化 new S_Y() , 让 requestSystem<S_X>()返回指向 S_Y 的相同指针.

我不能对 array of std::function 使用同样的技巧 ( std::is_base_of )检查继承因为类型被删除。此外,我打算不调用 new S_X() ,所以我无法通过其实例缓存类型并使用 dynamic_cast稍后。

这是 MCVE ( ideone )。
第一部分是Manager的定义:-

#include <iostream>
#include <vector>
#include <functional>
using namespace std;
class Manager{
public: Manager(){std::cout<<"Manager"<<std::endl; }
};
class EntityManager : public Manager{
public: EntityManager(){std::cout<<"EntityManager"<<std::endl;}
};
class AdvanceEntityManager : public EntityManager{
public: int testField=5; //just for testing
public: AdvanceEntityManager(){
std::cout<<"AdvanceEntityManager"<<std::endl;
}
};

这是类型操纵器:-

template<class T> class DummyHook{ public: static int dummyInt;  };
template<class T> class IndexCache{ public: static int index; };
//^ index = index of T* inside "globalManagerList"
std::vector<Manager*> globalManagerList;
std::vector<std::function<Manager*(int)>>* getFunctionPointers(){
static std::vector<std::function<Manager*(int)>> cacher;
return &cacher;
}
/** it is called indirectly by "requestSystem" */
template<class T> int indirectCall(){
std::function<Manager*(int)> func=[](int assignIndex){
IndexCache<T>::index = assignIndex;
auto re= new T();
globalManagerList.push_back(re);
return re;
};
getFunctionPointers()->push_back(func);
int dummy=42;return dummy;
}
template<class T> T* requestSystem(){
int k=DummyHook<T>::dummyInt;
//^ optimized out, but force calling "indirectCall()" before main() at @
return static_cast<T*>(globalManagerList[IndexCache<T>::index]);
}
template<class T> int DummyHook<T>::dummyInt = indirectCall<T>(); //@
template<class T> int IndexCache<T>::index = -42;

这是主要功能:-

int main() {
auto fs=getFunctionPointers();
int n=0;
for(auto ele: *fs){
ele(n); ++n;
//^ call every function in
// static std::vector<std::function<Manager*(int)>> cacher
}
std::cout<<"All initialized, ready!"<<std::endl;
auto entityManagerPtr=requestSystem<EntityManager>();
auto advanceManagerPtr=requestSystem<AdvanceEntityManager>();
//^ In this program, they are different instance, but I want it to be the same instance.
std::cout<<"Should be 5 : "<<advanceManagerPtr->testField<<std::endl;
return 0;
}

这是输出(两个管理器都被实例化了 :( ):-

Manager
EntityManager
Manager
EntityManager
AdvanceEntityManager
All initialized, ready!
Should be 5 : 5

最佳答案

类型的基列表在运行时不可用,而这实际上是您的代码所需要的。

但是我们可以编写自己的类型系统。在这种情况下,我们用每个父项的列表标记每个类型,并在该列表上启用编译时反射。

然后我们在设置工厂的时候,也把父类型的工厂替换掉,保证只使用子类型。

这里有一些随机工具可以提供帮助:

template<class...Ts> struct types_t {};
template<class T> struct tag_t { constexpr tag_t() {} using type=T; };
template<class T> constexpr tag_t<T> tag{};
template<class Tag> using type_t=typename Tag::type;

template<std::size_t...Is>
auto index_over( std::index_sequence<Is...> ) {
return [](auto&& f)->decltype(auto) {
return decltype(f)(f)( std::integral_constant<std::size_t, Is>{}... );
};
}
template<std::size_t N>
auto index_upto( std::integral_constant<std::size_t, N> ={} ) {
return index_over( std::make_index_sequence<N>{} );
}
template<class F>
auto invoke_foreach( F&& f ) {
return [f=std::forward<F>(f)](auto&&...args){
using discard=int[];
(void)discard{0,(void(
f(decltype(args)(args))
),0)...};
};
}
template<class F>
auto invoke_on_tuple( F&& f ) {
return [f=std::forward<F>(f)](auto&& tuple)->decltype(auto) {
using Tuple = decltype(tuple);
using dTuple = std::decay_t<Tuple>;
using Size = std::tuple_size<dTuple>;
return index_upto<Size{}>()( [&](auto&&...args)->decltype(auto){
return f( std::get<decltype(args){}>( decltype(tuple)(tuple) )... );
});
};
}
template<class...Ts>
constexpr std::tuple< tag_t<Ts>... > as_tuple_tags( types_t<Ts...> ) { return std::make_tuple(tag<Ts>...); }

template<class F>
struct y_combinator_t {
F f;
template<class...Args>
decltype(auto) operator()( Args&&... args ) {
return f(*this, std::forward<Args>(args)...);
}
};
template<class F>
y_combinator_t<std::decay_t<F>> y_combinate( F&& f ) { return {std::forward<F>(f)}; }

现在,我们用他们的 parent 标记类型:

class Manager{
public: Manager(){std::cout<<"Manager"<<std::endl; }
};
class EntityManager : public Manager{
public:
int base_value = 3;
EntityManager(){std::cout<<"EntityManager"<<std::endl;}
using parents = types_t<Manager>;
};
class AdvanceEntityManager : public EntityManager{
public: int testField=5; //just for testing
public:
AdvanceEntityManager(){
std::cout<<"AdvanceEntityManager"<<std::endl;
base_value = 1;
}
using parents = types_t<EntityManager>;
};

并使用起始码让我们轻松与家长合作:

template<class T, class Parents = typename T::parents>
auto foreach_parent( tag_t<T> ) {
constexpr auto parent_tuple = as_tuple_tags( Parents() );
return [](auto&& f) {
return invoke_on_tuple(invoke_foreach(f))(decltype(parent_tuple){});
};
}

template<class T, class...Ts>
auto foreach_parent( tag_t<T>, Ts&&... ) {
return [](auto&& f) {};
}

我们设置了一个双条目缓存,一个单例工厂,并使用了智能指针:

template<class T> class IndexCache{
public:
static int index;
static int factory;
};
template<class T> class Dummy{ public: static int index; };

using pManager = std::shared_ptr<Manager>;
using ManagerFactory = std::function<pManager()>;
using Task = std::function<void()>;

std::vector<pManager> globalManagerList;
std::vector<Task>& getManagerFactories(){
static std::vector<Task> cacher{};
return cacher;
}

template<class T>
ManagerFactory singleton_factory() {
return []{
static auto single = (void(std::cout << "making " << typeid(T).name() << " singlton" << std::endl), std::make_shared<T>());
return single;
};
}

并修改间接调用以替换父工厂任务:

template<class T>
void indirectCall(){
std::cout << "Setting up " << typeid(T).name() << std::endl;
auto func=[](auto tag){
return [tag](){
IndexCache<type_t<decltype(tag)>>::index = globalManagerList.size();
globalManagerList.push_back(singleton_factory<T>()());
};
};
//std::cout << "Adding " << typeid(T).name() << " factory " << std::endl;
IndexCache<T>::factory = getManagerFactories().size();
getManagerFactories().push_back(func(tag<T>));
auto replace_parents = y_combinate(
[&](auto& replace_parents, auto child_tag) {
foreach_parent(child_tag)([&](auto parent_tag){
using Parent = type_t<decltype(parent_tag)>;
std::cout << "Replacing " << typeid(Parent).name() << " factory with " << typeid(T).name() << " factory" << std::endl;
getManagerFactories()[IndexCache<Parent>::factory] = func(tag<Parent>);
replace_parents( parent_tag );
});
}
);
replace_parents(tag<T>);
std::cout << "Added " << typeid(T).name() << " factory " << std::endl;
}

requestSystem 中,我们确保所有设置都已完成:

void setup_code() {
for (auto&& factory:getManagerFactories())
factory();
}
void setup() {
static int unused = (setup_code(),7);
(void)unused;
}
template<class T>
T* requestSystem()
{
int dummy = Dummy<T>::index;
(void)dummy;
std::cout << "Requesting " << typeid(T).name() << std::endl;
setup();
return static_cast<T*>(globalManagerList[IndexCache<T>::index].get());
}

template<class T> int IndexCache<T>::index = -1;
template<class T> int IndexCache<T>::factory = -1;
template<class T> int Dummy<T>::index = (indirectCall<T>(), 7);

然后我们测试它:

int main() {
std::cout<<"All initialized, ready!"<<std::endl;
auto entityManagerPtr=requestSystem<EntityManager>();
//std::cout<<"Phase 1"<<std::endl;
(void)entityManagerPtr;
auto advanceManagerPtr=requestSystem<AdvanceEntityManager>();
//std::cout<<"Phase 2"<<std::endl;
//^ In this program, they are different instance, but I want it to be the same instance.
std::cout<<"Should be 5 : "<<advanceManagerPtr->testField<<std::endl;
std::cout<<"Should be 1 : "<<entityManagerPtr->base_value<<std::endl;
return 0;
}

这里我们请求entityManagerPtr,我们得到了advanceManagerPtr。从日志中可以看出,只创建了一个对象,其entityManagerPtr->base_value为1。

Live example .

关于c++ - std::function 检查类的继承,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43168297/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com