类型擦除技术 [英] Type erasure techniques

查看:132
本文介绍了类型擦除技术的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

(类型擦除,我的意思是隐藏类的一些或所有类型信息,有点像 Boost.Any 。)

我想获得一种类型的擦除技术,同时也分享那些我知道的。我的希望是有点想找到一些疯狂的技术,有人想到在他/她最黑暗的小时。 :)



我知道的第一个也是最明显的,常用的方法是虚函数。只需在基于接口的类层次结构中隐藏类的实现。许多Boost库都会执行此操作,例如 Boost.Any 会执行此操作来隐藏您的类型和 Boost.Shared_ptr 执行此操作以隐藏(de)分配机制。



然后有一个函数指针指向模板函数的选项,同时将实际对象保存在 void * 指针,例如 Boost.Function 确实会隐藏函子的真实类型。



所以,对于我的实际问题:

你知道什么其他类型的擦除技术?



编辑 br>
(因为我不确定要添加这个答案,或者只是编辑问题,我只是做一个更安全的。)

另一个很好的技术,隐藏实际类型的没有虚函数或 void * fiddling,是一个GMan雇用这里,与相关我的问题




示例代码:

  include< iostream> 
#include< string>

//注意:类名表示底层类型擦除技术

//这个行为类似于Boost.Any类型w.r.t.实现细节
类Any_Virtual {
struct holder_base {
virtual〜holder_base(){}
virtual holder_base * clone()const = 0;
};

template< class T>
struct holder:holder_base {
holder()
:held_()
{}

持有者(T const& t)
: hold_(t)
{}

virtual〜holder(){
}

virtual holder_base * clone()const {
return新持有者< T>(* this);
}

T held_;
};

public:
Any_Virtual()
:storage_(0)
{}

Any_Virtual(Any_Virtual const& other)
:storage_(other.storage _-> clone())
{}

template< class T>
Any_Virtual(T const& t)
:storage_(new holder< T>(t))
{}

〜Any_Virtual(){
明确();
}

Any_Virtual& operator =(Any_Virtual const& other){
Clear();
storage_ = other.storage _-> clone();
return * this;
}

template< class T>
Any_Virtual& operator =(T const& t){
Clear();
storage_ = new holder< T>(t);
return * this;
}

void Clear(){
if(storage_)
delete storage_;
}

template< class T>
T& As(){
return static_cast< holder< T> *>(storage _) - > held_;
}

private:
holder_base * storage_;
};

//以下演示使用void指针
//和模板操作函数的函数指针
//以安全地隐藏类型

枚举操作{
CopyTag,
DeleteTag
};

template< class T>
void Operate(void * const& in,void *& out,Operation op){
switch(op){
case CopyTag:
out = new T(* static_cast< ; T *>(in));
return;
case DeleteTag:
delete static_cast< T *>(out);
}
}

class Any_VoidPtr {
public:
Any_VoidPtr()
:object_(0)
,operate_ 0)
{}

Any_VoidPtr(Any_VoidPtr const& other)
:object_(0)
,operate_(other.operate_)
{
if(other.object_)
operate_(other.object_,object_,CopyTag);
}

template< class T>
Any_VoidPtr(T const& t)
:object_(new T(t))
,operate _(& Operate< T>)
{}

〜Any_VoidPtr(){
Clear();
}

Any_VoidPtr& operator =(Any_VoidPtr const& other){
Clear();
operate_ = other.operate_;
operate_(other.object_,object_,CopyTag);
return * this;
}

template< class T>
Any_VoidPtr& operator =(T const& t){
Clear();
object_ = new T(t);
operate_ =& Operate< T> ;;
return * this;
}

void Clear(){
if(object_)
operate_(0,object_,DeleteTag);
object_ = 0;
}

template< class T>
T& as(){
return * static_cast< T *>(object_);
}

private:
typedef void(* OperateFunc)(void * const&,void *&,Operation);

void * object_;
OperateFunc operate_;
};

int main(){
Any_Virtual a = 6;
std :: cout<< a.As< int>()<< std :: endl;

a = std :: string(oh hi!);
std :: cout<< a.As< std :: string>()<< std :: endl;

Any_Virtual av2 = a;

Any_VoidPtr a2 = 42;
std :: cout<< a2.As< int>()<< std :: endl;

Any_VoidPtr a3 = a.As< std :: string>();
a2 = a3;
a2.As< std :: string>()+ = - again!;
std :: cout<< a2:<< a2.As< std :: string>()<< std :: endl;
std :: cout<< a3:<< a3.As< std :: string>()<< std :: endl;

a3 = a;
a3.As< Any_Virtual>()。As< std :: string>()+ = -
std :: cout<< a:<< a.As< std :: string>()<< std :: endl;
std :: cout<< a3-> a:<< a3.As< Any_Virtual>()。As< std :: string>()< std :: endl;

std :: cin.get();
}


解决方案

使用函数指针(用于行为)和 void * (用于数据)。 不同的方法在它们添加语义糖的方式上简单地不同。虚拟函数例如只是

的语义糖

  struct Class {
struct vtable {
void * dtor)(Class *);
void(* func)(Class *,double);
} * vtbl
};

iow:函数指针。



说,有一种我特别喜欢的技术:它是 shared_ptr< void> ,只是因为它打击了那些不知道你可以做的人的心:可以在 shared_ptr< void> 中存储任何数据,并且在结尾仍然有正确的析构函数,因为 shared_ptr 构造函数是一个函数模板,并且将使用默认情况下为创建删除器传递的实际对象的类型:

  {
const shared_ptr< void> sp(new A);
} //调用A ::〜A()here

当然,只是通常的 void * /函数指针类型擦除,但很方便包装。


(With type erasure, I mean hiding some or all of the type information regarding a class, somewhat like Boost.Any.)
I want to get a hold of type erasure techniques, while also sharing those, which I know of. My hope is kinda to find some crazy technique that somebody thought of in his/her darkest hour. :)

The first and most obvious, and commonly taken approach, that I know, are virtual functions. Just hide the implementation of your class inside an interface based class hierarchy. Many Boost libraries do this, for example Boost.Any does this to hide your type and Boost.Shared_ptr does this to hide the (de)allocation mechanic.

Then there is the option with function pointers to templated functions, while holding the actual object in a void* pointer, like Boost.Function does to hide the real type of the functor. Example implementations can be found at the end of the question.

So, for my actual question:
What other type erasure techniques do you know of? Please provide them, if possible, with an example code, use cases, your experience with them and maybe links for further reading.

Edit
(Since I wasn't sure wether to add this as an answer, or just edit the question, I'll just do the safer one.)
Another nice technique to hide the actual type of something without virtual functions or void* fiddling, is the one GMan employs here, with relevance to my question on how exactly this works.


Example code:

#include <iostream>
#include <string>

// NOTE: The class name indicates the underlying type erasure technique

// this behaves like the Boost.Any type w.r.t. implementation details
class Any_Virtual{
        struct holder_base{
                virtual ~holder_base(){}
                virtual holder_base* clone() const = 0;
        };

        template<class T>
        struct holder : holder_base{
                holder()
                        : held_()
                {}

                holder(T const& t)
                        : held_(t)
                {}

                virtual ~holder(){
                }

                virtual holder_base* clone() const {
                        return new holder<T>(*this);
                }

                T held_;
        };

public:
        Any_Virtual()
                : storage_(0)
        {}

        Any_Virtual(Any_Virtual const& other)
                : storage_(other.storage_->clone())
        {}

        template<class T>
        Any_Virtual(T const& t)
                : storage_(new holder<T>(t))
        {}

        ~Any_Virtual(){
                Clear();
        }

        Any_Virtual& operator=(Any_Virtual const& other){
                Clear();
                storage_ = other.storage_->clone();
                return *this;
        }

        template<class T>
        Any_Virtual& operator=(T const& t){
                Clear();
                storage_ = new holder<T>(t);
                return *this;
        }

        void Clear(){
                if(storage_)
                        delete storage_;
        }

        template<class T>
        T& As(){
                return static_cast<holder<T>*>(storage_)->held_;
        }

private:
        holder_base* storage_;
};

// the following demonstrates the use of void pointers 
// and function pointers to templated operate functions
// to safely hide the type

enum Operation{
        CopyTag,
        DeleteTag
};

template<class T>
void Operate(void*const& in, void*& out, Operation op){
        switch(op){
        case CopyTag:
                out = new T(*static_cast<T*>(in));
                return;
        case DeleteTag:
                delete static_cast<T*>(out);
        }
}

class Any_VoidPtr{
public:
        Any_VoidPtr()
                : object_(0)
                , operate_(0)
        {}

        Any_VoidPtr(Any_VoidPtr const& other)
                : object_(0)
                , operate_(other.operate_)
        {
                if(other.object_)
                        operate_(other.object_, object_, CopyTag);
        }

        template<class T>
        Any_VoidPtr(T const& t)
                : object_(new T(t))
                , operate_(&Operate<T>)
        {}

        ~Any_VoidPtr(){
                Clear();
        }

        Any_VoidPtr& operator=(Any_VoidPtr const& other){
                Clear();
                operate_ = other.operate_;
                operate_(other.object_, object_, CopyTag);
                return *this;
        }

        template<class T>
        Any_VoidPtr& operator=(T const& t){
                Clear();
                object_ = new T(t);
                operate_ = &Operate<T>;
                return *this;
        }

        void Clear(){
                if(object_)
                        operate_(0,object_,DeleteTag);
                object_ = 0;
        }

        template<class T>
        T& As(){
                return *static_cast<T*>(object_);
        }

private:
        typedef void (*OperateFunc)(void*const&,void*&,Operation);

        void* object_;
        OperateFunc operate_;
};

int main(){
        Any_Virtual a = 6;
        std::cout << a.As<int>() << std::endl;

        a = std::string("oh hi!");
        std::cout << a.As<std::string>() << std::endl;

        Any_Virtual av2 = a;

        Any_VoidPtr a2 = 42;
        std::cout << a2.As<int>() << std::endl;

        Any_VoidPtr a3 = a.As<std::string>();
        a2 = a3;
        a2.As<std::string>() += " - again!";
        std::cout << "a2: " << a2.As<std::string>() << std::endl;
        std::cout << "a3: " << a3.As<std::string>() << std::endl;

        a3 = a;
        a3.As<Any_Virtual>().As<std::string>() += " - and yet again!!";
        std::cout << "a: " << a.As<std::string>() << std::endl;
        std::cout << "a3->a: " << a3.As<Any_Virtual>().As<std::string>() << std::endl;

        std::cin.get();
}

解决方案

All type erasure techniques in C++ are done with function pointers (for behaviour) and void* (for data). The "different" methods simply differ in the way they add semantic sugar. Virtual functions, e.g., are just semantic sugar for

struct Class {
    struct vtable {
        void (*dtor)(Class*);
        void (*func)(Class*,double);
    } * vtbl
};

iow: function pointers.

That said, there's one technique I particularly like, though: It's shared_ptr<void>, simply because it blows the minds off of people who don't know you can do this: You can store any data in a shared_ptr<void>, and still have the correct destructor called at the end, because the shared_ptr constructor is a function template, and will use the type of the actual object passed for creating the deleter by default:

{
    const shared_ptr<void> sp( new A );
} // calls A::~A() here

Of course, this is just the usual void*/function-pointer type erasure, but very conveniently packaged.

这篇关于类型擦除技术的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆