一、问题
在某个场景下需要实现一个类似于Tuple的类,主要是用于类指针的创建。直接使用Tuple其实是可以的,但一开始没有想清楚,所以后来绕了一圈儿后还是回到了类Tuple的实现。下面把这个实现的过程简要说明分析一下。
二、实现过程
实现的过程其实并不复杂,利用一个变参模板类展开即可:
template <typename... T> class CmdObj;
template <> class CmdObj<> {};
template <typename T, typename... N> class CmdObj<T, N...> : public CmdObj<N...> {
public:
using Base = CmdObj<N...>;
CmdObj() : Base() {
T *ptr = new T();
if (ptr != nullptr) {
m_t = ptr;
}
}
~CmdObj() = default;
public:
Base *getBase() { return this; }
public:
T *m_t;
};
代码很简单,其实就是一个变量模板类的实现。
三、获取对象和分析
在创建完成对象指针后,就可以获取其内部的指针了:
template <int id, typename T> class CmdObjElement {
public:
using Type = typename CmdObjElement<id - 1, typename T::Base>::Type;
};
template <typename T> class CmdObjElement<0, T> {
public:
using Type = T;
};
template <int id, typename T> constexpr auto &GetCmdObj(T &t) {
using Type = typename CmdObjElement<id, T>::Type;
return static_cast<Type &>(t).m_t;
}
将上面的两部分代码合并在一起,写一个例程,然后编译:
#include <iostream>
#include <memory>
#include <tuple>
#include <type_traits>
template<typename ... T>
class CmdObj;
/* First instantiated from: insights.cpp:11 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
class CmdObj<B, C, D> : public CmdObj<C, D>
{
public:
using Base = CmdObj<C, D>;
inline CmdObj()
: CmdObj<C, D>()
{
B * ptr = new B();
if(ptr != nullptr) {
this->m_t = ptr;
}
}
inline ~CmdObj() noexcept = default;
public:
inline CmdObj<C, D> * getBase();
public:
B * m_t;
};
#endif
/* First instantiated from: insights.cpp:11 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
class CmdObj<C, D> : public CmdObj<D>
{
public:
using Base = CmdObj<D>;
inline CmdObj()
: CmdObj<D>()
{
C * ptr = new C();
if(ptr != nullptr) {
this->m_t = ptr;
}
}
inline ~CmdObj() noexcept = default;
public:
inline CmdObj<D> * getBase();
public:
C * m_t;
};
#endif
/* First instantiated from: insights.cpp:11 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
class CmdObj<D> : public CmdObj<>
{
public:
using Base = CmdObj<>;
inline CmdObj()
: CmdObj<>()
{
D * ptr = new D();
if(ptr != nullptr) {
this->m_t = ptr;
}
}
inline ~CmdObj() noexcept = default;
public:
inline CmdObj<> * getBase();
public:
D * m_t;
};
#endif
/* First instantiated from: insights.cpp:57 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
class CmdObj<A, B, C, D> : public CmdObj<B, C, D>
{
public:
using Base = CmdObj<B, C, D>;
inline CmdObj()
: CmdObj<B, C, D>()
{
A * ptr = new A();
if(ptr != nullptr) {
this->m_t = ptr;
}
}
inline ~CmdObj() noexcept = default;
public:
inline CmdObj<B, C, D> * getBase();
public:
A * m_t;
};
#endif
template<>
class CmdObj<>
{
public:
};
template<typename T, typename ... N>
class CmdObj<T, N...> : public CmdObj<N...>
{
public:
using Base = CmdObj<N...>;
inline CmdObj()
: Base{}
{
T * ptr = new T{};
if(ptr != nullptr) {
this->m_t = ptr;
}
}
inline ~CmdObj() = default;
public:
inline Base * getBase()
{
return this;
}
public:
T * m_t;
};
template<int id, typename T>
class CmdObjElement
{
public:
using Type = typename CmdObjElement<id - 1, typename T::Base>::Type;
};
/* First instantiated from: insights.cpp:30 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
class CmdObjElement<0, CmdObj<B, C, D> >
{
public:
using Type = CmdObj<B, C, D>;
};
#endif
/* First instantiated from: insights.cpp:30 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
class CmdObjElement<1, CmdObj<B, C, D> >
{
public:
using Type = CmdObjElement<0, CmdObj<C, D> >::CmdObj<C, D>;
};
#endif
/* First instantiated from: insights.cpp:30 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
class CmdObjElement<0, CmdObj<C, D> >
{
public:
using Type = CmdObj<C, D>;
};
#endif
/* First instantiated from: insights.cpp:30 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
class CmdObjElement<2, CmdObj<B, C, D> >
{
public:
using Type = CmdObjElement<0, CmdObj<D> >::CmdObj<D>;
};
#endif
/* First instantiated from: insights.cpp:30 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
class CmdObjElement<1, CmdObj<C, D> >
{
public:
using Type = CmdObjElement<0, CmdObj<D> >::CmdObj<D>;
};
#endif
/* First instantiated from: insights.cpp:30 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
class CmdObjElement<0, CmdObj<D> >
{
public:
using Type = CmdObj<D>;
};
#endif
/* First instantiated from: insights.cpp:37 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
class CmdObjElement<0, CmdObj<A, B, C, D> >
{
public:
using Type = CmdObj<A, B, C, D>;
};
#endif
/* First instantiated from: insights.cpp:37 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
class CmdObjElement<1, CmdObj<A, B, C, D> >
{
public:
using Type = CmdObjElement<0, CmdObj<B, C, D> >::CmdObj<B, C, D>;
};
#endif
/* First instantiated from: insights.cpp:37 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
class CmdObjElement<2, CmdObj<A, B, C, D> >
{
public:
using Type = CmdObjElement<0, CmdObj<C, D> >::CmdObj<C, D>;
};
#endif
/* First instantiated from: insights.cpp:37 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
class CmdObjElement<3, CmdObj<A, B, C, D> >
{
public:
using Type = CmdObjElement<0, CmdObj<D> >::CmdObj<D>;
};
#endif
template<typename T>
class CmdObjElement<0, T>
{
public:
using Type = T;
};
template<int id, typename T>
inline constexpr auto & GetCmdObj(T & t)
{
using Type = typename CmdObjElement<id, T>::Type;
return static_cast<Type &>(t).m_t;
}
#ifdef INSIGHTS_USE_TEMPLATE
template<>
inline constexpr A *& GetCmdObj<0, CmdObj<A, B, C, D> >(CmdObj<A, B, C, D> & t)
{
using Type = CmdObjElement<0, CmdObj<A, B, C, D> >::CmdObj<A, B, C, D>;
return static_cast<CmdObjElement<0, CmdObj<A, B, C, D> >::CmdObj<A, B, C, D> &>(t).m_t;
}
#endif
#ifdef INSIGHTS_USE_TEMPLATE
template<>
inline constexpr B *& GetCmdObj<1, CmdObj<A, B, C, D> >(CmdObj<A, B, C, D> & t)
{
using Type = CmdObjElement<0, CmdObj<B, C, D> >::CmdObj<B, C, D>;
return static_cast<CmdObjElement<0, CmdObj<B, C, D> >::CmdObj<B, C, D> &>(t).m_t;
}
#endif
#ifdef INSIGHTS_USE_TEMPLATE
template<>
inline constexpr C *& GetCmdObj<2, CmdObj<A, B, C, D> >(CmdObj<A, B, C, D> & t)
{
using Type = CmdObjElement<0, CmdObj<C, D> >::CmdObj<C, D>;
return static_cast<CmdObjElement<0, CmdObj<C, D> >::CmdObj<C, D> &>(t).m_t;
}
#endif
#ifdef INSIGHTS_USE_TEMPLATE
template<>
inline constexpr D *& GetCmdObj<3, CmdObj<A, B, C, D> >(CmdObj<A, B, C, D> & t)
{
using Type = CmdObjElement<0, CmdObj<D> >::CmdObj<D>;
return static_cast<CmdObjElement<0, CmdObj<D> >::CmdObj<D> &>(t).m_t;
}
#endif
struct A
{
inline void test()
{
std::operator<<(std::cout, "this is A!").operator<<(std::endl);
}
};
struct B
{
inline void test()
{
std::operator<<(std::cout, "this is b!").operator<<(std::endl);
}
};
struct C
{
inline void test()
{
std::operator<<(std::cout, "this is c!").operator<<(std::endl);
}
};
struct D
{
inline void test()
{
std::operator<<(std::cout, "this is d!").operator<<(std::endl);
}
};
void Display()
{
std::shared_ptr<CmdObj<A, B, C, D> > x = std::make_shared<CmdObj<A, B, C, D> >();
A * xA = GetCmdObj<0>(static_cast<const std::__shared_ptr_access<CmdObj<A, B, C, D>, 2, false, false>&>(x).operator*());
xA->test();
B * xB = GetCmdObj<1>(static_cast<const std::__shared_ptr_access<CmdObj<A, B, C, D>, 2, false, false>&>(x).operator*());
xB->test();
C * xC = GetCmdObj<2>(static_cast<const std::__shared_ptr_access<CmdObj<A, B, C, D>, 2, false, false>&>(x).operator*());
xC->test();
D * xD = GetCmdObj<3>(static_cast<const std::__shared_ptr_access<CmdObj<A, B, C, D>, 2, false, false>&>(x).operator*());
xD->test();
}
int main()
{
Display();
return 0;
}
有一个好工具就是好用啊,分析代码也方便太多了。有了编译前后的代码比较,再结合模板的相关知识,应该是一目了然了。
四、总结
到最后的实现仍然是使用了类似std::tuple的实现,通过索引ID来获取不同类型的具体的指针。其实最初是想的通过一个基类形成的静态容器返回然后通过多态来达到对不同类型的控制。可惜的是模板的匹配总是无法通过,只好改成一个动态生成动态获取的过程。后来又想通过在遍历的时候儿拿到对象指针而非固定写到指定的索引上,毕竟这样就无法修改对象生成的顺序了。可又无法解决不同对象回传的问题(通过基类返回直接就崩溃了)。故而目前只能还是先使用这种索引控制来得到具体的对象指针,以后有机会再优化它。