fat std :: function

时间:2018-03-06 14:42:52

标签: c++ function c++11

我试图将我的对象打包到64个字节(自行开发的属性),我需要保存getter和setter成员函数。

我真的很喜欢std :: function但是很大:

sizeof(std::function<int(void)>)给出40个字节(VS2017,x64)。

我试图弄清楚是否有std::function 16字节大的精简版本并且内部没有额外的分配?

更新:解决方案如下所示,两个成员函数需要16个字节(x64)。 成员函数指针变成了模板参数,所以它们什么都不带,只存储对象指针和vptr。

感谢@Yakk。

class Property
{
public:
    virtual ~Property() {}

    virtual QVariant value() const = 0;
    virtual void setValue(const QVariant& value) = 0;
};

template<typename TYPE,
         typename CLASS,
         TYPE(CLASS::*get)() const,
         void(CLASS::*set)(const TYPE &)>
class PropertyValue : public Property
{
public:
    PropertyValueGet() = delete;
    PropertyValueGet(PropertyValueGet const& ) = delete;
    PropertyValueGet& operator=(PropertyValueGet const& ) = delete;

    PropertyValueGet(CLASS* object) : m_object(object)  {}

    TYPE getValue() const {
        return (m_object->*get)();
    }

    void setValue(const TYPE& value) {
        (m_object->*set)(value);
    }

    // Property implementation
    //
    virtual QVariant value() const final {
        QVariant v = QVariant::fromValue<TYPE>(getValue());
        return v;
    }

    virtual void setValue(const QVariant& value) final {
        setValue(value.value<TYPE>());
    }

private:
    CLASS* m_object = nullptr;
};

5 个答案:

答案 0 :(得分:4)

m_getter的类型为decltype(m_getter)

在Modern C ++中使用std::mem_fn是一个坏主意 - lambda表达式是一种更清晰,更透明(对开发人员和编译器而言)绑定参数的方式。

Stephan T. Lavavej在谈话“functional: What's New, And Proper Usage"中提到了与lambdas相比std::bindstd::mem_fn的一些缺点:

memfn problems #0

memfn problems #1

bind problems

您应该使用 lambda表达式。 E.g:

auto my_getter = [](Foo& foo){ return foo.getter(); };

Foo some_foo;
my_getter(some_foo);

Foo some_foo;
auto my_getter = [&some_foo](){ return some_foo.getter(); };

my_getter();

答案 1 :(得分:3)

<div id="contatoPage"> <div id="contatoEmail" > <div class="row"> <div class="emailForm"> <form role="form" action="Contato.php" method="post" > <div class="form-group"> <label for="name"> Nome:</label> <input type="text" class="form-control" id="name" name="name" required maxlength="50"> </div> <div class="form-group"> <label for="email"> Email:</label> <input type="email" class="form-control" id="email" name="emailCliente" required maxlength="50"> </div> <div class="form-group"> <label for="name"> Assunto:</label> <input type="text" class="form-control" id="name" name="assunto" required maxlength="50"> </div> <div class="form-group"> <label for="name"> Message:</label> <textarea class="form-control" type="textarea" name="message" id="message" placeholder="Escreva sua mensagem aqui" maxlength="6000" rows="7"></textarea> </div> <button type="submit" name="envEmail" class="botaoEnviar pull-right" id="btnContactUs">Enviar! &rarr;</button> </form> <?php if(isset($_POST['envEmail'])) { $nome= $_POST['name']; $emailcliente= $_POST['emailCliente']; $assunto= $_POST['assunto']; $menssagem= $_POST['message']; $to = 'jun@paconstrushop.com.br'; //can receive notification $subject = $assunto; $message = 'Email enviado por:'.$nome."\n".$menssagem; $headers = 'From:'.$emailcliente. "\r\n" . 'Reply-To: jun@paconstrushop.com.br' . "\r\n" . 'X-Mailer: PHP/' . phpversion(); if(@mail($to, $subject, $message, $headers)) { echo " <div id=success_message style=width:100%; height:100%; > <h3>Sent your message successfully!</h3> </div>"; } else { echo "<div id=error_message style=width:100%; height:100%; > <h3>Error</h3> Sorry there was an error sending your form. </div>"; } } ?> </div> </div> </div> 有一个类型,&Foo::getter最有可能TYPE(Foo::*)(void)更小的类型。同样,std::function<TYPE(void)>&Foo::setter

但是你可以通过void(Foo::*)(TYPE)放弃使用&Foo::field的{​​{1}}错误概念获取甚至更小

TYPE Foo::*

另外附带一个实例

template<typename T, typename C>
struct Property
{
    Property(T C::*member) : member(member) {}
    T & get(C & c) { return std::invoke(member, c); }
    void set(C & c, T & t) { std::invoke(member, c) = t; }
private:
    T C::* member;
}

答案 2 :(得分:2)

这是一个答案,因为在中执行此操作非常烦人。

这是一个“简单的”无状态视图,仅限std :: function。

// utility tag type for dispatching
template<class Tag>
struct tag_t {};

// helper type to find the "caller" function pointer used to erase invoke:
template<class Sig>
struct caller_type;
template<class R, class...Args>
struct caller_type<R(Args...)> {
    using type = R(*)(void*, Args&&...);
};

template<class Sig>
using caller = typename caller_type<Sig>::type;

// make a caller<Sig> that type erases calling T with Sig:
template<class T, class Sig>
struct make_caller;
template<class T, class R, class...Args>
struct make_caller<T, R(Args...)> {
  caller<R(Args...)> operator()() const {
    return [](void* ptr, Args&&...args)->R {
      return (*static_cast<T*>(ptr))( std::forward<Args>(args)... );
    };
  }
};
template<class T, class...Args>
struct make_caller<T, void(Args...)> {
  caller<void(Args...)> operator()() const {
    return [](void* ptr, Args&&...args)->void {
      (*static_cast<T*>(ptr))( std::forward<Args>(args)... );
    };
  }
};

// provides operator() overload compatible with Sig,
// then dispatches the call down through a derived type D:
template<class D, class Sig>
struct call_dispatch;

template<class D, class R, class...Args>
struct call_dispatch<D, R(Args...)> {
  R operator()(Args...args)const {\
    auto* caller = self()->get_caller(tag_t<R(Args...)>{});
    return (*caller)( self()->pvoid(), std::forward<Args>(args)... );
  }
  auto self() { return static_cast<D*>(this); }
  auto self() const { return static_cast<D const*>(this); }
};

// stores a function pointer to invoke Sig
template<class Sig>
struct call_storage {
  caller<Sig> f = nullptr;
  template<class T>
  static call_storage make() {
    return {make_caller<T, Sig>{}()};
  }
  caller<Sig> get_caller( tag_t<Sig> ) const { return f; }
};

// a table of such function pointers
template<class...Sig>
struct call_vtable:call_storage<Sig>... {
  template<class T>
  static call_vtable make() {
    return {call_storage<Sig>::template make<T>()...};
  }
  using call_storage<Sig>::get_caller...;
};

// overload helper to dispatch to correct Sig:
template<class D, class...Sig>
struct call_dispatcher:
  call_dispatch<D, Sig>...
{
  using call_dispatch< D, Sig >::operator()...;
};

// Erases invoking but not storing an arbitrary type T
// with all of ...Sig.  Stores the invokers inside itself,
// not in an external vtable, to increase locality at the cost
// of per-instance size:
template<class... Sig>
struct call_view_t:
  private call_vtable<Sig...>,
  call_dispatcher< call_view_t<Sig...>, Sig... >
{
  template<class F,
    std::enable_if_t<!std::is_same<std::decay_t<F>, call_view_t>{}, bool> =true
  >
  call_view_t( F&& f ):
    call_vtable<Sig...>( call_vtable<Sig...>::template make<std::decay_t<F>>() ),
    ptr( std::addressof(f) )
  {}        
  call_view_t()=default;
  call_view_t(call_view_t const&)=default;
  call_view_t& operator=(call_view_t const&)=default;
  explicit operator bool() const { return ptr != nullptr; }
  void* pvoid() const { return ptr; }

  using call_vtable<Sig...>::get_caller;
private:
  void* ptr = 0;
};

它需要2个指针,一个用于存储非拥有状态,另一个用于存储调用者(它还支持调用的任意数量的签名)。

我们可以创建一个新的call_view类型,而不是存储ptr*,存储一块内存;在该块中,放置new是传入的F的状态,pvoid()返回指向它的指针。

除非你将自己局限于琐碎的破坏和复制/移动,否则你还必须存储指向函数的指针。

由于call_impl仅根据所存储事物的类型而不是其值而有所不同,我们可以将f存储在vtable中,并且仅从{{继承} 1}}。在vtable中,我们可以存储call_dispatch的副本的复制/移动/销毁。

这允许我们使用一个状态F,只需要在std::function中放置任何状态的存储器上需要1个指针。

请注意,上面的代码可能有拼写错误,并不能解决您的问题。它有9/10件可以解决你的问题。

std::function

并尝试// a vtable that represents "I can be copied or moved and destroyed": struct copyable_vtable { void(*dtor)(void*)=0; void(*copy)(void* dest, void const* src)=0; void(*move)(void* dest, void* src)=0; template<class T> static copyable_vtable make() { return { [](void* ptr){ static_cast<T*>(ptr)->~T(); }, [](void* dest, void const* src){ ::new( dest ) T(*static_cast<T const*>(src)); }, [](void* dest, void * src){ ::new( dest ) T(std::move(*static_cast<T const*>(src))); } }; } }; // the vtable that our "small function" needs: template<class...Sig> struct small_func_vtable: copyable_vtable, call_vtable<Sig...> { template<class T> static small_func_vtable make() { return { copyable_vtable::template make<T>(), call_vtable<Sig...>::template make<T>() }; } template<class T> static small_func_vtable const* get() { static const auto vtable = make<T>(); return &vtable; } }; // bundles up the size and alignment requirements: template<std::size_t S=sizeof(void*)*3, std::size_t A=alignof(void*)> struct size_params { enum { size = S, align = A }; }; // A small stack allocated std::function that refuses to get bigger // If you try to construct it with something larger, you get // a static assert failure. Also supports arbitrary number of // overloads of (): template<class Size, class...Sig> struct small_function: call_dispatcher< small_function<Size, Sig...>, Sig... > { private: small_func_vtable<Sig...> const* vtable = 0; mutable std::aligned_storage_t< Size::size, Size::align > data; public: template<class F, std::enable_if_t<!std::is_same<std::decay_t<F>, small_function>{}, bool> =true > small_function( F&& f ) { static_assert( sizeof(std::decay_t<F>)<=Size::size ); static_assert( alignof(std::decay_t<F>)<=Size::align ); ::new( (void*)&data ) std::decay_t<F>( std::forward<F>(f) ); vtable = small_func_vtable<Sig...>::template get<std::decay_t<F>>(); } small_function()=default; // we could refactor this into base class: small_function(small_function const& o) { if (!o.vtable) return; o.vtable->copy( pvoid(), o.pvoid() ); vtable = o.vtable; } small_function(small_function&& o) { if (!o.vtable) return; o.vtable->move( pvoid(), o.pvoid() ); vtable = o.vtable; } small_function& operator=(small_function const& o) { if (this == &o) return *this; if (vtable) { vtable->dtor(pvoid()); vtable = nullptr; } if (o.vtable) { o.vtable->copy( pvoid(), o.pvoid() ); vtable = o.vtable; } return *this; } small_function& operator=(small_function&& o) { if (this == &o) return *this; if (vtable) { vtable->dtor(pvoid()); vtable = nullptr; } if (o.vtable) { o.vtable->move( pvoid(), o.pvoid() ); vtable = o.vtable; } return *this; } // use null vtable to determine if we are empty: explicit operator bool() const { return vtable != nullptr; } // these must be visible to dispatch system. I think // they are harmless to expose to end users, so I don't bother // with making them private and friending dispatch system: void* pvoid() const { return &data; } template<class S> caller<S> get_caller(tag_t<S> t) const { if (!vtable) return nullptr; return vtable->get_caller(t); } }; template<class...Sig> using simple_small_function = small_function< size_params<>, Sig... >;

Live example

答案 3 :(得分:2)

OP澄清说使用的成员是固定的:

String allWordHelper(){

    String holder = " ";
    for (Map.Entry<Character, DictionaryTree> child : children.entrySet()) {
        if(completeWord != null){
            //holder += completeWord + child.getValue().allWordHelper();
            Word_Holder.allWords.add(completeWord);

        }else{
            holder += child.getValue().allWordHelper();
        }

    }

    return holder;
}

每个template<class T> struct generic_property { virtual T getValue() const = 0; virtual void setValue(T const&) = 0; protected: virtual ~generic_property() {} }; template<class D, class T, void(D::*set)(T const&), T(D::*get)() const> struct property:generic_property<T> { T getValue() const final { return (self->*get)(); } void setValue(T const& t) final { (self->*set)(t); } property( D* s ):self(s) {} // cannot usually safely copy/move/trivial: property() = delete; property( property const& ) = delete; property& operator=( property const& ) = delete; private: D* self = 0; }; struct Bob { void setFoo( int const& i ) { std::cout << i << " set\n"; } int getFoo() const { std::cout << 42 << " get\n"; return 42; } property<Bob, int, &Bob::setFoo, &Bob::getFoo> foo; Bob():foo(this) {} }; 占用1个指针空间(因此32到64位,或4-8个字节,具体取决于操作系统的位宽)。

添加propertyvirtual会占用另一个指针空间。

live example

我们可以在generic_property<T>中手动键入擦除,而不是虚拟继承。

答案 4 :(得分:0)

让我们看一下std::function所需的内容:

  1. 指向如何复制,销毁和调用有效负载的列表的指针。了解目标类型以及如何检索它是一种额外的好处,每种类型的额外成本是微不足道的(每个实例都没有)。
  2. 目标。为了避免猖獗的分配,至少应该有一个成员函数指针的空间。不幸的是,MSVC使用的符合格式是一个大的4个指针(它们在不同的情况下使用不同的较小的不符合的指针)。其他ABI使用较小的ABI来完全发挥作用。
  3. 所以,这是5个指针值=&gt; 5 * 8 = 64位Windows上的40字节,至少可以容纳成员函数指针。