我实现了一个std::function-like类,
另外,
new,并且只存储一个指针。operator()代码绘制。调用在编译时解析。delete与大对象new对称。要使用它,您可以制作一张地图或设备,以便在适当的位置构建或移动构造。然后可以使用R v = it->invoke<R, T, A1, A2...>(A1, A2...)对存储的实例执行R T::operator()(A1,A2...)。
请注意,它实际上允许将许多不同类型的lambda存储在同一个容器(deque/map/etc)中,因此不能与std::function直接比较。
问题:
#include <unordered_map>
#include <vector>
#include <memory>
#include <cstdint>
#include <set>
#include <deque>
#include <algorithm>
#include <functional>
#include <utility>
// totalSize is the size of the entire object, not the payload
// the actual payload size is less than that by sizeof pointer
template<std::size_t totalSize = 128>
class CallableStorage
{
public:
static constexpr std::size_t smallSize = totalSize -
sizeof(std::size_t(*)(void*));
using Storage =
typename std::aligned_storage<smallSize>::type;
template<typename T>
CallableStorage(T&& fn)
: CallableStorage(typename std::integral_constant<
bool, sizeof(T) <= smallSize>::type(),
std::move(fn))
{
}
CallableStorage(CallableStorage const&) = delete;
CallableStorage& operator=(CallableStorage const&) = delete;
CallableStorage(CallableStorage&& r)
: size(r.size)
, helper(r.helper)
{
MovePair places;
if (size <= smallSize)
{
places.from = &r;
places.to = this;
r.helper(HelperCommand::Move, &places);
}
else
{
payload.largeStorage = r.payload.largeStorage;
r.payload.largeStorage = nullptr;
}
}
CallableStorage& operator=(CallableStorage&&) = delete;
~CallableStorage()
{
if (size <= smallSize)
helper(HelperCommand::Dispose, &payload.smallStorage);
else
helper(HelperCommand::Dispose, payload.largeStorage);
}
template<typename T>
T* lambdaPointer()
{
return lambdaPointer<T>(
typename std::integral_constant<
bool, sizeof(T) <= smallSize>::type());
}
template<typename T>
T const* lambdaPointer() const
{
return lambdaPointer<T>(
typename std::integral_constant<
bool, sizeof(T) <= smallSize>::type());
}
template<typename R, typename T, typename... Args>
R invoke(Args&& ...args)
{
return invoke<R, T, Args...>(
typename std::integral_constant<
bool, sizeof(T) <= smallSize>::type(),
std::forward<Args>(args)...);
}
private:
enum class HelperCommand
{
Dispose, // pointer to object
Move // pointer to MovePair
};
struct MovePair
{
void* to;
void* from;
};
template<typename R, typename T, typename... Args>
R invoke(std::true_type, Args&& ...args)
{
auto ptr = reinterpret_cast<T*>(&payload.smallStorage);
return ptr->operator()(std::forward<Args>(args)...);
}
template<typename R, typename T, typename... Args>
R invoke(std::false_type, Args&& ...args)
{
auto ptr = reinterpret_cast<T*>(payload.largeStorage);
return ptr->operator()(std::forward<Args>(args)...);
}
template<typename T>
CallableStorage(std::true_type, T&& fn)
: size(sizeof(T))
, helper(lambdaHelper<T>)
{
new (&payload.smallStorage) T(std::move(fn));
}
template<typename T>
CallableStorage(std::false_type, T&& fn)
: size(sizeof(T))
, helper(lambdaHelper<T>)
{
payload.largeStorage = new T(std::move(fn));
}
template<typename T>
T* lambdaPointer(std::true_type)
{
return reinterpret_cast<T*>(&payload.smallStorage);
}
template<typename T>
T const* lambdaPointer(std::true_type) const
{
return reinterpret_cast<T const*>(&payload.smallStorage);
}
template<typename T>
T* lambdaPointer(std::false_type)
{
return reinterpret_cast<T*>(payload.largeStorage);
}
template<typename T>
T const* lambdaPointer(std::false_type) const
{
return reinterpret_cast<T const*>(payload.largeStorage);
}
template<typename T>
static std::size_t lambdaHelper(HelperCommand cmd, void* p)
{
if (p)
{
using IsSmall = typename std::integral_constant<bool,
sizeof(T) <= smallSize>::type;
switch (cmd)
{
case HelperCommand::Dispose:
lambdaDisposerImpl<T>(IsSmall(), p);
break;
case HelperCommand::Move:
auto const& move = *reinterpret_cast<MovePair const*>(p);
lambdaMoveImpl<T>(IsSmall(), move);
break;
}
}
return sizeof(T);
}
template<typename T>
static void lambdaDisposerImpl(std::true_type, void* p)
{
reinterpret_cast<T*>(p)->~T();
}
template<typename T>
static void lambdaDisposerImpl(std::false_type, void* p)
{
delete reinterpret_cast<T*>(p);
}
template<typename T>
static void lambdaMoveImpl(std::true_type, MovePair const& p)
{
CallableStorage& lhs = *reinterpret_cast<CallableStorage*>(p.to);
CallableStorage& rhs = *reinterpret_cast<CallableStorage*>(p.from);
T* from = reinterpret_cast<T*>(&rhs.payload.smallStorage);
T* to = reinterpret_cast<T*>(&lhs.payload.smallStorage);
new (to) T(std::move(*from));
}
template<typename T>
static void lambdaMoveImpl(std::false_type, MovePair const& )
{
}
union Payload
{
Storage smallStorage;
void* largeStorage;
};
Payload payload;
std::size_t size;
std::size_t (*helper)(HelperCommand, void*);
};发布于 2018-06-05 09:16:33
的转发引用
在这里,我们对可能是lvalue引用的类型使用std::move():
template<typename T>
CallableStorage(T&& fn)
: CallableStorage(typename std::integral_constant<
bool, sizeof(T) <= smallSize>::type(),
std::move(fn))
{
}我们必须使用std::forward<T>,并且/或约束T为rvalue引用类型:
static_assert(std::is_rvalue_reference<T&&>::value,
"CallableStorage requires a moveable argument");我们已经失去了operator()的许多便利,因为我们现在必须为invoke()指定模板参数。我认为最好将函数签名作为类型的一部分,除非对异构函数存储有明确的需求。目前,我们似乎把太多的知识强加给了用户。
在计算size的大小时,我们似乎忽略了Storage成员。它应该是
static constexpr std::size_t smallSize =
totalSize - sizeof (helper_type) - sizeof (std::size_t);(为了给helper命名类型,我在我的副本中到处乱动,我认为这是值得的,以减少事故)
这里只需说明一下注释- sizeof pointer中的术语,因为函数指针不一定与C++中的对象指针大小相同。
我们包含了太多的标准库头(但对于std::aligned_storage,我们仍然忽略了我们确实需要的)。我相信这些都是必需的:
#include <cstdint>
#include <type_traits>
#include <utility>https://codereview.stackexchange.com/questions/127807
复制相似问题