26 Aralık 2018 Çarşamba

std::make_unique metodu

std::make_unique
C++14 ile geliyor. std::unique_ptr yaratılmasına yardımcı olur. Bu metod bir şekilde C++11'de gözden kaçmış.

std::make_unique ve array
Açıklaması şöyle.
make_unique distinguishes T from T[] and T[N], unique_ptr(new ...) does not.

You can easily get undefined behaviour (UB) by passing a pointer that was new[]ed to a unique_ptr<T>, or by passing a pointer that was newed to a unique_ptr<T[]>.
Örnek
Array için şöyle tanımlanır.
auto p3 = std::make_unique<char[]>(3);           // C++14
sd::make_unique Biraz Daha Yavaştır
make_unique tüm belleği value initialize eder. İçi şöyledir. new char[size]() kısmına dikkat!
unique_ptr<T>(new typename std::remove_extent<T>::type[size]())
//                                                         ~~~~
Perfect Forwarding
std::make_unique perfect forwarding yapar. Bu kural gereği lvalue bir nesne, referans olarak iletilir. Yani int x tipinde bir lvalue değişken varsa, int& olarak iletilir.

Perfect Forwarding işlemi, std::make_unique veya çağırdığı yardımcı bir metod ile yapılabilir.

make_uniqye  ve constructor parametreleri
Parametreleri std::make_unique() metoduna geçeriz. Şöyle yaparız.
struct position
{
  int x, y;

  position(int x, int y) : x(x), y(y) {}
};

class positioned
{
public:
  positioned(int x, int y) : pos(x, y) {}
private:
  position pos;
};

auto bla = std::make_unique<positioned>(1,2);
make_unique ve exception
Açıklaması şöyle.
C++14 introduced std::make_unique because, as a result of the parameter evaluation order not being specified, this was unsafe:
Elimizde şöyle bir kod olsun
f(std::unique_ptr<MyClass>(new MyClass(param)), g()); // Syntax A
Açıklama şöyle devam ediyor.
Explanation: if the evaluation first allocates the memory for the raw pointer, then calls g() and an exception is thrown before the std::unique_ptr construction, then the memory is leaked.)

Calling std::make_unique was a way to constrain the call order, thus making things safe:
C++14 ile şöyle yaparız
f(std::make_unique<MyClass>(param), g());             // Syntax B

Örnek
Elimizde şöyle bir metod olsun.
process(std::unique_ptr<object>(new object), iMightThrow());
Eğer new object() çağrıldıktan sonra iMightThrow() çağrılır ve exception atarsa memory leak olur.

Şöyle kullanırsak iMightThrow()  exception atsa bile memory leak olmaz.
process(std::make_unique<object>(), iMightThrow());
make_unique metodunu friend yapmak
Burada ilginç bir fikir var. Her platformda çalışacağı garanti değil ancak yine de not almak istedim.
#include <memory>

template <typename T>
class A
{
public:
    //declare make_unique as a friend
    friend std::unique_ptr<A<T>> std::make_unique<A<T&>>();


    static std::unique_ptr<A> CreateA(T x)
    {
        return std::make_unique<A>(x);
    }

protected:
    A(T x) { (void)x; }
};

int main()
{
    std::unique_ptr<A<int>> a = A<int>::CreateA(5);
    (void)a;
    return 0;
}






25 Aralık 2018 Salı

std::unique_ptr ve Custom Deleter

Giriş
Scott Meyer’in “Effective Modern C++” kitabındaki açıklama şöyle.
Deleters that are function pointers generally cause the size of a std::unique_ptr to grow from one word to two. For deleters that are function objects, the change in size depends on how much state is stored in the function object. Stateless function objects (e.g., from lambda expressions with no captures) incur no size penalty, and this means that when a custom deleter can be implemented as either a function or a captureless lambda expression, the lambda is preferable.
custom deleter metodu olarak lambda
custom deleter lambda olarak tanımlanabilir.
Örnek
Stack'te yaratılan bir değişkeni silmemek için hiçbir şey yapmayan bir custom deleter şöyle tanımlanır.
auto noop = [](int*){};
std::unique_ptr<int, decltype(noop)>intptr(&a, noop);
loglama yapan bir custom deleter şöyle tanımlanır.
auto deleter_ = [](int *p) { doSth(p); delete p; };
std::unique_ptr<int, decltype(deleter_)> up(new int, deleter_);
Örnek
Şöyle yaparız.
void foo(bool & currState, bool newState)
{
  bool prevState = currState;
  currState      = newState;
  std::unique_ptr<bool, std::function<void(bool*)>> txEnder(&prevState,
    [&prevState, &currState](bool* p) {
      currState = prevState;
  });
  cout << "currState: " << currState << endl;
}
custom deleter metodu olarak functor
Örnek
map<int,int*> olarak düşünürsek int* yerine unique_pte gelirse şöyle yaparız.
struct Deleter {
  void operator()(int* i) { delete i; }
};

std::map<int, std::unique_ptr<int, Deleter>> m;

void foo(int* i) {
    m[0] = std::unique_ptr<int, Deleter>(i);
}
Örnek
Stack'te yaratılan bir değişkeni silmemek için hiçbir şey yapmayan bir custom deleter şöyle tanımlanır.
struct D{

  void operator()(int* p) const {
    std::cout << "Deleter \n";
    //... no actual delete
  }
};  

int a =5;    
std::unique_ptr<int,D> intptr (&a);
custom deleter olarak free
Şöyle yaparız.
struct free_deleter {
  template <typename T> void operator()(T* p) const {
    ::free(p);
  }
};
template <typename T> using malloc_ptr = std::unique_ptr<T, free_deleter>;
Şöyle yaparız.


malloc_ptr<char> ptr (static_cast<char*>(::malloc(100)));