14 Temmuz 2017 Cuma

new operator için üretilen kod

Giriş
"new operator" ve "operator new" farklı şeylerdir. Ve bence çok kötü bir isimlendirme yapılmış.

"operator new" aynı C'deki malloc gibi raw bellek alanı döndürür. Şöyle yaparız
char *x = static_cast<char *>(operator new(100));//malloc ile aynıdır
"new operator" ise C++ dilindeki bir anahtar kelimedir. Bu anahtar kelimeyi gören derleyici kod üretir. Şöyle yaparız
my_class *x = new my_class(0);
Bu yüzden "new operator" için 3 tane overload vardır.

new operator için üretilen kod
Not : Bu yazıda nesnemiz için user defined new operator olmadığını etmediğini farz ettim.

Kodlarken genellikle new T() şeklinde bir satır yazıp geçeriz. Arka planda neler döndüğüne pek dikkat etmeyiz. Aslında new T() kodunun çalışması için arka planda derleyici bizim için kod üretmektedir. Bu kod iki ana esasa dayanır.


1. Global operator new
Yaratmak istediğimiz nesne için heap'ten alan almak gerekir. Bunun için derleyici önce
T *p = ::operator new(sizeof(T)); // allocate memory for a T
çağrısı ile aynı malloc'taki gibi bellek alanı ayıran kod parçası ayrılır.

2. Contructor Çağrısı
Daha sonra da T nesnesinin constructor metodunu çağıran kod parçasını üretiyor.
Bu kod ise şuna benziyor.
Not : Aslında aşağıdaki kod placement new kodudur fakat kavramı göstermesi için almakta mahzur görmedim.
new (p) T; // construct a T into the allocated memory

3. Cast
Ayrılan bellek alanı istediğimiz nesne tipine cast edilerek bize verilir. Bu iş için kod üretilmiyor ancak mantıksa olarak işlemin bir parçası olduğu için eklemek istedim.

Sonuç
new T() çağrısı, global operator new - yani ::new()'den  -  farklıdır !

Global operator new ile derleyicinin ürettiği kodun farkını gösteren örnek aşağıda.
// Allocates space for a T, and calls T's constructor,
// or calls a user-defined overload of new.
//
T* v = new T;

// Allocates space for N instances of T, and calls T's 
// constructor on each, or calls a user-defined overload
// of new[]
//
T* v = new T[N];

// Simply returns a raw byte array of `sizeof(T)` bytes.
// No constructor is invoked.
//
void* v = ::operator new(sizeof(T));
new operator İçin Üretilen Kod
Şuna benzer.
template <typename T, typename... Ts>
T* library_new(Ts&&... xs)
{
  auto* ptr = /* request enough memory for `T` from OS */;
  new (ptr) T(std::forward<Ts>(xs)...);
  return ptr;
}

template <typename T>
void library_delete(T* ptr)
{
  ptr->~T();
    /* reclaim memory for `T` from OS */
} 
Nesnenin Heap'te Yaratılmasını Önlemek
Bunun için sadece new operator'ünü saklamak yeterli.
private:
    void* operator new(size_t);          // standard new
    void* operator new(size_t, void*);   // placement new
    void* operator new[](size_t);        // array new
    void* operator new[](size_t, void*); // placement array new
Aynı zamanda delete operatürünü de engellemek gerekebilir. Ama yazmadım.

13 Temmuz 2017 Perşembe

[[fallthrough]] Attribute

Giriş
break cümlesinin bilinçli olarak konulmadığını belirtir.

Örnek
Şöyle yaparız.
switch (0) {
  case 0:
    std::cout << "a\n";
    [[fallthrough]];
  case 1:
    std::cout << "b\n";
    break;
}
Çıktı olarak şunu alırız.
a
b

std::u16string Sınıfı

Giriş
Şu satırı dahil ederiz.
#include <string>
String için std::u16string sınıfı da tanımlı. Char yerine 2 byte'lık char16_t tutması dışında geri kalan herşey aynı.

Constructor
Örnek
Şöyle yaparız.
std::u16string str = u"...";
Örnek
Şöyle yaparız.
#include <boost/locale.hpp>

// Create the strings
std::u16string str1 = boost::locale::conv::utf_to_utf<char16_t>("unicode");
std::u16string str2 = boost::locale::conv::utf_to_utf<char16_t>("UNICODE");
c_str metodu
Şöyle yaparız.
std::u16string str = ...;
char16_t *str2 = (char16_t *) str.c_str();

STL Sort Algoritmaları

Giriş
STL algoritmalarını işlevlerine göre gruplamak anlamayı çok daha kolaylaştırıyor. Aşağıdaki algoritmalar sıralı dizileri yine sıralı olarak birleştirme algoritmalarını gösteriyor.

partition
Algoritma şuna benzer.
template<class ForwardIt, class UnaryPredicate>
ForwardIt partition(ForwardIt first, ForwardIt last, UnaryPredicate p)
{
    if (first == last) return first;
    ForwardIt part(first++);
    if (first == last) return p(*part) ? first : part;
    while (first != last) {
        if (p(*part))
            ++part;
        else if (p(*first)) {
            iter_swap(part, first);
            ++part;
        }
        ++first;
    }
    return part;
}
Açıklaması şöyle
Complexity
Exactly std::distance(first,last) applications of the predicate and at most std::distance(first,last) swaps. If ForwardIt meets the requirements of BidirectionalIterator at most std::distance(first,last)/2 swaps are done.
Yani n tane eleman varsa ForwardIterator kullanıldığı için en fazla n-1 tane swap yapılır. Eğer BidirectionalIterator kullanılırsa daha az swap yapılır.

lambda ile kullanım örneği
Elimizde şöyle bir liste olsun. hello olmayan elemanları listenin sonuna atmak isteyelim.
list<string> s;
Liste şöyle bölümlenir.
std::partition(
    std::begin(s),
    std::end(s),
    [](const string &w){return w != "hello";});
partition_point
std::partition_point yazısına taşıdım.

stable_partition
std::stable_partition yazısına taşıdım.

nth_element
std::nth_element yazısına taşıdım.

partial_sort
Örnek yaz

partial_sort_copy
Girdi olarak kullanılan veri yapısının sırasını değiştirmez. Sıralanmış veri çıktı olarak verilen veri yapısına kopyalanır.

sort
std::sort yazısına taşıdım

stable_sort
Açıklaması şöyle
O(N·log^2(N)), where N = std::distance(first, last) applications of cmp. If additional memory is available, then the complexity is O(N·log(N)).
Eğer sıralanacak elemen sayısı değeri belirtilmemiş N'den küçükse bellekten tasarruf eden bir algoritma kullanılır. Eğer N'den büyükse zamandan tasarrruf eden bir algoritma kullanılır.
İmzası şöyle
template<class RandomAccessIterator>
void stable_sort(RandomAccessIterator first, RandomAccessIterator last);

template<class RandomAccessIterator, class Compare>
void stable_sort(RandomAccessIterator first, RandomAccessIterator last,
  Compare comp);


10 Temmuz 2017 Pazartesi

valgrind heap profiler - massif

Giriş
Aracın ismi massif.  Bir uygulamanın çalıştığı süre boyunca ne kadar heap kullandığını ölçer.Çıktıyı görmek için ms_print veya massif-visualizer kullanılabilir.

Örnek
Şöyle yaparız.
valgrind --tool=massif command
Çıktıyı görmek için şöyle yaparız
$ ms_print massif.out
--------------------------------------------------------------------------------
  n        time(i)         total(B)   useful-heap(B) extra-heap(B)    stacks(B)
--------------------------------------------------------------------------------
 75 26,935,731,596       22,420,728       21,956,875       463,853            0

Örnek
Şöyle yaparız.
dd if=/dev/zero bs=1M | valgrind --tool=massif ./myapp
Örnek
Bir uygulamanın en fazla ne kadar heap kullandığını ölçmek için şöyle yaparız.
valgrind --tool=massif --pages-as-heap=yes --massif-out-file=massif.out ./myapp;
grep mem_heap_B massif.out |
sed -e 's/mem_heap_B=\(.*\)/\1/' |
sort -g |
tail -n 1