Giriş
Şu satırı dahil ederiz
1. Function Pointer
2. Function Template
3 function_view - STL değil. Kodu şöyle
std::function'a Atanabilecek Nesneler
Açıklama şöyle
Hepsinin kullanımını görmek için şöyle yaparız
Şeklen şöyledir.
Contructor - F
İmzası şöyle. F nesnesinin kopyasını saklar.
std::function ve Lambda yazısına taşıdım.
Contructor - Allocator + F
!operator
Şöyle çağırırız.
Şu satırı dahil ederiz
#include <functional>
std::functon'a alternatif olabilecek seçenekler şöyle1. Function Pointer
2. Function Template
3 function_view - STL değil. Kodu şöyle
std::function'a Atanabilecek Nesneler
Açıklama şöyle
There are different kinds of callable's, namely functions and functors. std::function was designed to not mater which of those you give to itAçıklaması şöyle
Can hold a functor, member function pointer, function pointer or lambdaÖrnek
Hepsinin kullanımını görmek için şöyle yaparız
class Foo {};
class Bar {};
class FunctorEx
{
public:
bool operator()(Bar const&, Foo const&)
{
return true;
}
} FunctorExInst;
class MemFunction
{
public:
bool MemFunctionEx(Bar const&, Foo const&)
{
return true;
}
} MemFunctionInst;
bool FunctionEx(Bar const&, Foo const&)
{
return true;
}
int main()
{
auto LambdaEx = [] (Bar const&, Foo const&) -> bool
{
return true;
};
std::function<bool(Bar const&, Foo const&)> exFunctionVar;
exFunctionVar = std::bind(&MemFunction::MemFunctionEx, &MemFunctionInst,
std::placeholders::_1, std::placeholders::_2);
exFunctionVar = FunctorExInst;
exFunctionVar = FunctionEx;
exFunctionVar = LambdaEx;
}
Tanımlama - C++98Şeklen şöyledir.
std::function<R(A, B, C, ...)>
Kodda ise şöyledirtemplate<class R, class... ArgTypes>
class function<R(ArgTypes...)> {
template<class F> function(F f);
};
Önce return type, sonra parantez içinde function'a geçilen parametreler belirtilir.
std::function<void(const char *)
Bir başka örnekstd::function<int (int)> myFunc;
Eğer parametreleri belirtmezsek istediğimiz sayıda ve tipte parametre geçebiliriz. Şöyle yaparız.
setCallback (std::bind (foo,1));
setCallback (std::bind (foo,1,2));
Tanımlama - C++11
std::function<void()>> myFunc;
Bu yöntemi bir projede callback çağırmak için kullandık. Callback'leri şöyle bağladık.setCallback (std::bind (foo,1));
setCallback (std::bind (foo,1,2));
Tanımlama - C++11
Şöyle yaparız.
using myFunc = std::function<int(const Foo&)>;
Metodlarİmzası şöyle. F nesnesinin kopyasını saklar.
template< class F >
function( F f );
Açıklaması şöyle.Requires: F shall be CopyConstructible. f shall be Callable for argument types ArgTypes and return type R. [...]Örnek - lambda
std::function ve Lambda yazısına taşıdım.
Contructor - Allocator + F
İmzası şöyle.
C++17 örneğinde allocator kullanılmıyor. std::function kendi içinde bellek ayırıyor. lambda ve std::bind farklı miktarlarda bellek gerektirirler. Bunu görmek için şöyle yaparız.
Dolayısıyla mümkünse şöyle yapmak gerekir.
std::function nesnesini function pointer'a dönüştürür. Şu kod derlenmez.template <class F, class A> function(allocator_arg_t, const A& a, F f);
std::function ile allocator kullanılması C++11 ile eklendi. Daha sonra C++17 ile çıkarıldı. Açıklaması şöyle.Unfortunately, allocators for std::function has been dropped in C++17.Örnek
Now the accepted solution to avoid dynamic allocations inside std::function is to use lambdas instead of std::bind. That does work, at least in GCC - it has enough static space to store the lambda in your case, but not enough space to store the binder object.
C++17 örneğinde allocator kullanılmıyor. std::function kendi içinde bellek ayırıyor. lambda ve std::bind farklı miktarlarda bellek gerektirirler. Bunu görmek için şöyle yaparız.
MyCallBack cb;
std::cout<< sizeof(std::bind(&MyCallBack::Fire, &cb)) << "\n";
auto a = [&] { cb.Fire(); };
std::cout << sizeof(a);
Çıktı olarak 24 ve 8 alırız. Yani std::bind 24 byte veri tüketirken, lambda 8 byte veri tüketir. Dolayısıyla mümkünse şöyle yapmak gerekir.
std::function<void()> func = [&cb]{ cb.Fire(); };
// sizeof lambda is sizeof(MyCallBack*), which is small enough
Açıklaması şöyleAs a general rule, with most implementations, and with a lambda which captures only a single pointer (or a reference), you will avoid dynamic allocations inside std::function with this technique (it is also generally better approach as other answer suggests).target metodu
Keep in mind, for that to work you need guarantee that this lambda will outlive the std::function. Obviously, it is not always possible, and sometime you have to capture state by (large) copy. If that happens, there is no way currently to eliminate dynamic allocations in functions, other than tinker with STL yourself (obviously, not recommended in general case, but could be done in some specific cases).
std::function<int(int)> func = [](int x){return x;};
int(*Fptr)(int) = &func; //error
Ancak şöyle yapabiliriz. Burada T kullandım ancak yerine function imzasını yazmak yeterli. Eğer imzalar uyumlu değilse null döner.T *ptr_func = func.target<T>();
Gerçek koda bakalım. Elimizde şöyle bir function pointer tipi olsun olsun.
typedef void (*myhandlertype)(int);
Bu tiple uyumlu bir std::function olsun.
std::function<void (int)> func = ...;
Şöyle çeviririz.
myhandlertype* handler = func.target<void (int)>();
function nesnesinin dolu olup olmadığı anlaşılabilir. Şöyle yaparız.
std::function<int (int)> myFunc;
m_pInit = ...;
std::cout << !myFunc << std::endl; // True
==operator
Açıklaması şöyle
Diğer Notlar
std::function ve kendi virtual metodlarım
Şöyle yapabilirz. std::function doğru virtual metodu çağırır.
std::function strategy örütüntüsünü gerçekleştirmek için kullanılabilir. Bu durumda interface sınıfını kaybederiz ancak lambda kullanırken belki tercih edilebilir.
std::function ve overhead
st::function nesnesinin herhangi bir function pointer'a göre daha ağır olduğu kesin. En azından heap allocation ve bir virtual function çağrısı yapma ihtimali var. Bence ne kadar ağır olursa olsun, function pointer'dan her durumda daha kullanışlı.
Overload Metodlar
std::function overload edilmiş metodlar ile pek iyi çalışmıyor. Aşağıdaki kod derlenmez.
Örnek
Şöyle tanımlarız.
Şöyle yaparız.Compares a std::function with a null pointer. Empty functions (that is, functions without a callable target) compare equal, non-empty functions compare non-equal.
std::function<int (int)> myFunc;
if (myFunc == nullptr) {...}
std::function ve kendi virtual metodlarım
Şöyle yapabilirz. std::function doğru virtual metodu çağırır.
class foo
{
public:
foo()
{
this->f = std::bind(&foo::doSomething, this);
}
private:
virtual void doSomething(void) { }
private:
std::function<void(void)> f;
}
class bar : public foo
{
public:
bar(void) {}
private:
virtual void doSomething(void) override { }
}
std::function ve strategy örüntüsüstd::function strategy örütüntüsünü gerçekleştirmek için kullanılabilir. Bu durumda interface sınıfını kaybederiz ancak lambda kullanırken belki tercih edilebilir.
std::function ve overhead
st::function nesnesinin herhangi bir function pointer'a göre daha ağır olduğu kesin. En azından heap allocation ve bir virtual function çağrısı yapma ihtimali var. Bence ne kadar ağır olursa olsun, function pointer'dan her durumda daha kullanışlı.
Overload Metodlar
std::function overload edilmiş metodlar ile pek iyi çalışmıyor. Aşağıdaki kod derlenmez.
#include <functional>
void add(int,int){}
class A {};
void add (A, A){}
int main(){
std::function <void(int, int)> func = add;
}
static_cast ile overload metodu belirtmek gerekir.
std::function <void(int, int)> func = static_cast<void(*)(int, int)> (add);
std::function'ı metodlara parametre geçmek
Klasik nesne gibi value veya reference olarak geçilebilir.
Eğer default boş bir değer std::function tanımlamak istersek
1. Şöyle yapabiliriz.
std::function ve variadic template
std::function variadic template şeklinde kullanılabiliyor. Ortaya oldukça karışık kodlar çıkıyor. Bazı örnekler burada.Klasik nesne gibi value veya reference olarak geçilebilir.
void foo(std::function<...> f)
{
f(...);
}
veyavoid foo(const std::function<...>& f)
{
f(...);
}
Bence const& olanı tercih ediyorum.Eğer default boş bir değer std::function tanımlamak istersek
1. Şöyle yapabiliriz.
void func(const std::function<void()>& f = {}) {
if(f) f();
}
2. Şöyle yapabiliriz.void func(const std::function<void()>& f = nullptr)
{
if(f)
f();
}
3. Şöyle yapabiliriz.void func(const std::function<void()>& f = std::function<void()>()) {
try {
f();
}
catch(std::bad_function_call e) {
}
}
3. örnekte exception yakalanmasının sebebi şu. Eğer 2. örnekteki gibi if kontrolü yapsaydık exception yakalamaya gerek kalmazdı.§ 20.8.11.1 Class bad_function_call [func.wrap.badcall]1/ An exception of type bad_function_call is thrown by function::operator() (20.8.11.2.4) when the function wrapper object has no target.
Örnek
Şöyle yaparız.
std::function ve signaltemplate <typename ... Args>
void run_callback(std::function<void(Args...)> const & func, Args ... as) {
try {
func(as...);
} catch(const std::exception& ex) {
print_callback_error(ex.what());
} catch(const std::string& ex) {
print_callback_error(ex.c_str());
} catch(...) {
print_callback_error();
}
}
Şöyle tanımlarız.
template<class FuncType>
class MulticastFunction {
private:
vector<std::function<FuncType>> targets;
public:
void operator()() {
for(auto& target : this->targets) {
target();
}
}
void addTarget(FuncType& target) {
this->targets->push_back( target );
}
}
MulticastFunction<void()> mc;
mc.addTarget( foo );
mc.addTarget( bar );
mc();
Siteyi yeni keşfettim. Yazılarınızın kesilmemesi dileğiyle...
YanıtlaSil