- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
问题:
多个组件希望共享相同的不可变数据(任意类型),但每个组件都有一组不同的接口(interface)要求。
例如,组件 A 可能需要有一个名为 hash_code(const object&)
的可用函数,而组件 B 可能需要该对象有一个名为 type(const对象&)
.
如果组件 C 创建了一个指向某个对象 O 的共享指针,对于它有一个名为 hash_code
的可用函数和一个名为 type
的可用函数,那么组件 C 可以与组件 A 和 B 共享 O,因为可以推导出兼容的接口(interface)。
现在我们已经解耦了多态身份类型,没有复制开销。
我实现这个的方法是有一个模板句柄
类,它充满了方法描述符
(更多模板类):
这是一个完整的、有效的测试程序,有两种方法:
一个是手动写handle类,一个是让模板扩展给我写。
目前有两个问题:
必须有更优雅的方式......
#include <iostream>
#include <utility>
#include <string>
#include <typeindex>
#include <boost/functional/hash.hpp>
// some arbitrary type which supports hashing and type
struct algo1_ident
{
std::type_info const& _algo_type = typeid(algo1_ident);
std::string arg1;
std::string arg2;
};
auto type(algo1_ident const& ident) -> std::type_info const&
{
return ident._algo_type;
}
auto hash_code(algo1_ident const& ident) -> std::size_t
{
auto seed = ident._algo_type.hash_code();
boost::hash_combine(seed, ident.arg1);
boost::hash_combine(seed, ident.arg2);
return seed;
}
//
// manual approach
//
// step one: define a complete concept
struct algo_ident_concept
{
virtual std::type_info const & impl_type(const void* p) const = 0;
virtual std::size_t impl_hash_code(const void* p) const = 0;
};
//
// step two: define the model of that concept
//
template<class Impl>
struct algo_ident_model : algo_ident_concept
{
std::type_info const& impl_type(const void* p) const override
{
return type(ref(p));
}
std::size_t impl_hash_code(const void* p) const override
{
return hash_code(ref(p));
}
private:
static const Impl& ref(const void* p) {
return *static_cast<Impl const*>(p);
}
};
//
// step three: write the handle class
//
template<template <class> class Model, template <class> class PtrType>
struct algo_ident_handle
{
template<class Impl> struct model_tag {};
template<class Impl>
algo_ident_handle(std::shared_ptr<const Impl> ptr)
: _impl(std::move(ptr))
, _access_model(make_access_model(model_tag<const Impl>()))
{}
template<class Impl>
algo_ident_concept const* make_access_model(model_tag<Impl>)
{
static struct : algo_ident_concept
{
std::type_info const& impl_type(const void* p) const override
{
using ::type;
return type(ref(p));
}
std::size_t impl_hash_code(const void* p) const override
{
using ::hash_code;
return hash_code(ref(p));
}
private:
static const Impl& ref(const void* p) {
return *static_cast<Impl const*>(p);
}
} const _model {};
return std::addressof(_model);
}
PtrType<const void> _impl;
const algo_ident_concept* _access_model;
//
// interface
//
std::size_t hash_code() const {
return _access_model->impl_hash_code(_impl.get());
}
std::type_info const& type() const {
return _access_model->impl_type(_impl.get());
}
};
//
// now the componentised approach
//
// step 1: define the concept, model and handle interface for supporting the
// method `hash_code`
// This can go in a library
template<class Host>
struct has_hash_code
{
struct concept
{
virtual std::size_t hash_code(const void*) const = 0;
};
template<class Impl> struct model : virtual concept
{
std::size_t hash_code(const void* p) const override
{
using ::hash_code;
return hash_code(*static_cast<const Impl*>(p));
}
};
struct interface
{
std::size_t hash_code() const
{
auto self = static_cast<const Host*>(this);
return self->model()->hash_code(self->object());
}
};
};
// step 2: define the concept, model and handle interface for supporting the
// method `type`
// This can go in a library
template<class Host>
struct has_type
{
struct concept
{
virtual std::type_info const& type(const void*) const = 0;
};
template<class Impl> struct model : virtual concept
{
std::type_info const& type(const void* p) const override
{
using ::type;
return type(*static_cast<const Impl*>(p));
}
};
struct interface
{
std::type_info const& type() const
{
auto self = static_cast<const Host*>(this);
return self->model()->type(self->object());
}
};
};
// step 3: provide a means of turning a pack of methods into a concept base class
template<class Host, template<class>class...Methods>
struct make_concept
{
using type = struct : virtual Methods<Host>::concept... {};
};
// step 4: provide a means of turning a pack of methods into a model class
template<class Impl, class Host, template<class>class...Methods>
struct make_model
{
using concept_type = typename make_concept<Host, Methods...>::type;
using type = struct : Methods<Host>::template model<Impl>... , concept_type {};
static auto apply()
{
static const type _model {};
return std::addressof(_model);
}
};
// step 5: provide a means of turning a pack of methods into an interface
template<class Host, template<class>class...Methods>
struct make_interface
{
using type = struct : Methods<Host>::interface... {};
};
// step 6: convenience class in which to store the object pointer and the
// polymorphic model
template<class ConceptType>
struct storage
{
storage(std::shared_ptr<const void> object, const ConceptType* concept)
: _object(object), _model(concept)
{}
const void* object() const { return _object.get(); }
const ConceptType* model() const { return _model; }
std::shared_ptr<void const> _object;
const ConceptType* _model;
};
// step 7: build a handle which supports the required methods while
// storing a shared_ptr to the object
template<template<class> class...Methods>
struct handle
: make_interface<handle<Methods...>, Methods...>::type
{
using this_class = handle;
using concept_type = typename make_concept<this_class, Methods...>::type;
using storage_type = storage<concept_type>;
template<class Impl>
static auto create_storage(std::shared_ptr<Impl> ptr)
{
using model_type = typename make_model<Impl, this_class, Methods...>::type;
const model_type* pm = make_model<Impl, this_class, Methods...>::apply();
return storage_type(ptr, pm);
}
template<class Impl>
handle(std::shared_ptr<Impl> ptr)
: _storage(create_storage(ptr))
{}
const void* object() const { return _storage.object(); }
const concept_type* model() const { return _storage.model(); }
storage<concept_type> _storage;
};
//
// another arbitrary object which also supports the hash_code and type protocols
namespace algo2 {
struct algo2_ident
{
std::type_info const& _algo_type = typeid(algo2_ident);
std::string arg1 = "foo";
std::string arg2 = "bar";
};
auto type(algo2_ident const& ident) -> std::type_info const&
{
return ident._algo_type;
}
auto hash_code(algo2_ident const& ident) -> std::size_t
{
auto seed = ident._algo_type.hash_code();
boost::hash_combine(seed, ident.arg1);
boost::hash_combine(seed, ident.arg2);
return seed;
}
}
//
// test
//
int main(int argc, const char * argv[])
{
algo_ident_handle<algo_ident_model, std::shared_ptr> h1 = std::make_shared<const algo1_ident>();
algo_ident_handle<algo_ident_model, std::shared_ptr> h2 = std::make_shared<const algo2::algo2_ident>();
//
// prove that an h1 is equivalent to the object of which it is a handle
//
algo1_ident chk1 {};
std::cout << h1.hash_code() << std::endl;
std::cout << hash_code(chk1) << std::endl;
algo2::algo2_ident chk {};
std::cout << h2.hash_code() << std::endl;
std::cout << hash_code(chk) << std::endl;
//
// same proof for the composed handle
//
handle<has_hash_code, has_type> ht1 = std::make_shared<const algo2::algo2_ident>();
std::cout << ht1.hash_code() << std::endl;
std::cout << hash_code(chk) << std::endl;
return 0;
}
最佳答案
我可能会遗漏一些东西,但以下似乎可以完成工作:
// To avoid conflict with name and ADL.
namespace detail
{
template <typename T>
decltype(auto) callHashCode(T&& t) { return hash_code(std::forward<T>(t)); }
template <typename T>
decltype(auto) callType(T&& t) { return type(std::forward<T>(t)); }
}
class HashRunner
{
public:
template <typename T>
HashRunner(std::shared_ptr<T> p) :
hash_code([=](){ return detail::callHashCode(*p); })
{}
std::function<std::size_t()> hash_code;
};
class TypeRunner
{
public:
template <typename T>
TypeRunner(std::shared_ptr<T> p) :
type([=]() -> const std::type_info& { return detail::callType(*p); })
{}
std::function<const std::type_info&()> type;
};
template <typename ... Ts>
class MyHandle : public Ts...
{
public:
template <typename T>
MyHandle(std::shared_ptr<T> p) : Ts(p)... {}
};
Demo .
关于C++句柄/主体类自动由模板参数包组成。这可以改进吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38964136/
我设置了 Helm 柄和 Helm 柄。我有tiller-deploy。昨天,我可以定期运行了。但今天我收到此错误消息 Error: could not find a ready tiller pod
我以前已将分er安装到特定的 namespace 中。 我设置了一个环境变量来设置'tiller'命名空间-但我不记得该环境变量的名称-而且似乎无法通过网络搜索找到它。 这是什么 key ? 最佳答案
当我在 View 模型中使用如下界面时 class MainViewModel @ViewModelInject constructor( private val trafficImagesR
我正在尝试找到如何在某个 fragment 相关场景中定义 Hilt 的解决方案。我有以下设置: Activity 父 fragment 1 子 fragment 1 子 fragment 2 ...
Hilt 指出如果没有@Provides 注解就不能提供这个接口(interface): interface PlannedListRepository { fun getAllLists()
我的问题非常简单明了:两个注释/示例之间有什么区别: 例子一 @Singleton class MySingletonClass() {} @Module @InstallIn(FragmentCom
我是一名优秀的程序员,十分优秀!