C++11- nullptr != NULL

C++11 standartlarıyla beraber C++ programlama dili yavaş yavaş daha deterministik olmaya başladı. Yani belirsiz durumlar, kullanım hataları dilin standartlarının desteğiyle en aza çekiliyor. Bu kapsamda nullptr de uzun zamandır bilinen bir sorunu çözüyor.

#include <iostream>
#include <memory>


using namespace std;



void func(int x) 
{ 
    cout << "int" << endl; 
}

void func(char *y) 
{
    cout << "pointer" << endl; 
}
 
int main() {
    func(0);        // int overload
    func((char *)0); // char * overload
    func(nullptr);  // char* overload
    func(NULL);     // main.cpp:23:14: error: call of overloaded ‘func(NULL)’ is ambiguous (compiler: g++5)
}

Üstteki hata aslında derleyiciden derleyiciye farklı olabilir, örneğin visual studio da ‘int’ olarak yorumlanabilir. Kısacası non-deterministic bir durum sözkonusu, derleyicinden derleyiciye farkediyor.
Aslında sorun C++ programlama dilinin iki temel özelliğinden kaynaklanıyor; Function Overloading ve Otomatik Tür dönüşümü. Şimdi bir de NULL isminin nasıl tanımlandığına bakalım:

#if defined (_STDDEF_H) || defined (__need_NULL)
#undef NULL		/* in case <stdio.h> has defined it. */
#ifdef __GNUG__
#define NULL __null
#else   /* G++ */
#ifndef __cplusplus
#define NULL ((void *)0)
#else   /* C++ */
#define NULL 0
#endif  /* C++ */
#endif  /* G++ */
#endif	/* NULL not defined and <stddef.h> or need NULL.  */
#undef	__need_NULL

Görüldüğü üzere derleyiciniz cplusplus derleyicisi ise NULL ismi 0 olarak tanımlanmış. İşte tam da burda sıkıntı ortaya çıkıyor.

Derleyici ‘func(NULL)’ dediğinizde hangi fonksiyonu çağıracağını bilmiyor(g++5 için) ya da kullandığınız başka bir derleyici de farklı bir davranış gözlemleyebilirsiniz integer veya char * function overloadlarından biri çağırılabilir. Hülasası başta da belirttiğim gibi C++11 bu tarz non-deterministic konulara da el atıyor ve bu meseleyi nullptr ile çözüyor. Artık NULL yazdığınız her yere nullptr yazarak daha güvenli programlama yapmış olabilirsiniz özellikle cross-platform yazılım geliştiyorsanız.

nullptr’ nin aslında türü std::nullptr_t ve bir prvalue’ dur. Dolayısıyla herhangi bir şekilde deference edilemez.

Bunun yanı sıra nullptr meta programmingte perfect forwardinge de olanak sağlamaktadır. Şöyle ki:

#include <iostream>
#include <memory>


using namespace std;


template<class F, class A>
void foo(F f, A a)
{
    f(a);
}
 

void func(char *y) 
{
    cout << "pointer" << endl; 
}
 
int main() {
    func(0);        // int overload
    func((char *)0); // void * overload
    func(nullptr);  // void * overload
    
    //foo(func, NULL); //error: invalid conversion from ‘long int’ to ‘char*’ [-fpermissive]
    foo(func, nullptr);   
}

foo şablon fonksiyonunun ikinci parametresine NULL verirsek derleyici f fonksiyonunu integer bir değerle çağırmaya çalışacak. Çünkü şablon parametresi olarak NULL ‘ ın türü integer tespit edildi. nullptr ile çağırdığımızda ise türü std::nullptr_t olarak tespit edileceğinden sorunsuzca ‘f’ veya ‘func’ fonksiyonu sorunsuzca çağırılacaktır.

Aslında nullptr nin implementasyonu bir const class’dır. Merak edenler için standart taslağında implementasyonu şu şekildedir:


const // this is a const object...
class {
public:
 template<class T> // convertible to any type
 operator T*() const // of null non-member
 { return 0; } // pointer...
 template<class C, class T> // or any type of null
 operator T C::*() const // member pointer...
 { return 0; }
private:
 void operator&() const; // whose address can't be taken
} nullptr = {};

Advertisements

C++11 – Zeki, Akıllı Göstericiler (Smart Pointers)

C++ programlama dili, atası C dilinden aldığı özelliklerden biri de memory yönetimidir. Ham göstericilerle (raw pointer) rahatlıkla hafızayı yönetebilirsiniz. ‘new’ operatörü ile aldığınız bellek bloğunu kolaylıkla ‘delete’ operatörüyle iade edebilriz. Fakat bu beraberinde riskleri de beraberinde getiriyor.

Memoryye iade edilmeyen yani ‘delete’ edilmeyen nesneler üzerinden bu bellek bloğuna erişilmeye çalışıldığında çalışma zamanı hatalarıyla (run time error) karşılaşıyor, signal 11 (segmentation fault) gibi kernelden uyarılar alabilirsiniz. İşte bu noktada C++’ ın en güçlü yanlarından olan RAII’ ye dayanan dinamik memory tahsisini de nesnelerin ömürlerine bağlayan sınıflar tasarlandı. Kullanım amaçlarına göre farklılık genel olarak değişse de amaç; hatalara neden olan new, delete operatörleriyle açıktan açığa bellek tahsisini kodumuzda yapmamak.

auto_ptr (Deprecated):
C++ 98 standartlarıyla beraber dile katılan şablon sınıfı auto_ptr temel olarak bellek alanlarının tahsisi ve geri iadesi üzerine odaklandı. Fakat yanlış kullanımı nedeniyle memory hatalarından kaçalım diye tasarlanan akıllı göstericilerin amacının tam tersi şekilde memory hatasına neden oldu. Aşağıdaki kodu inceleyelim.

#include <iostream>
#include <memory>

using namespace std;

struct s
{
    int a;
    int c;
    string b;

    s(){cout << "S ctor" << endl;}
    ~s(){cout << "~S dtor" << endl;}
};

void func(auto_ptr<s> e)
{
    e->a = 99;
}

int main()
{
    auto_ptr<s> aptr(new s);

    cout  << "func begin" << endl;
    func(aptr);
    cout  << "func end" << endl;      

    cout << aptr->a << endl;

}


Örnek kodda aptr nesnesi ile ‘f’ fonksiyonu çağrıldğında copy ctor çağırılacak ve aynı nesneyi gösteren bir diğer auto_ptr nesnesi oluşturulacak. Ve bu nesnenin(e) ömrü fonksiyon blokları sonlandığında sonlanacağı için gösterdiği dinamik bellek alanını da free edecek. Dolayısıyla aptr nesnesi artık free edilmiş bir bellek alanını göstermektedir kısacası dangling pointer hatasına sebep olur. Program seg fault vererek aşağıdaki şekilde sonlanır:

S ctor
func begin
~S dtor
func end

RUN FINISHED; Segmentation fault; core dumped; real time: 90ms; user: 0ms; system: 0ms

auto_ptr sınıfı C++17 standartlarıyla beraber standart kütüphaneden çıkarılacaktır. Dolayısıyla kullanmayın .)

unique_ptr
auto_ptr nin yaptığı hatayı yapmaz, tek bir sahiplik vardır. Copy ctor, operator = fonksiyonlarını =delete şeklinde yasaklar dolayısıyla kullanıcı herhangi bir yerde herhangi bir şekilde nesneyi kopyalamaya çalıştığında derleme hatası alacaktır.

Yukarıdaki örneği unique_ptr ile yeniden yazarsak derleme zamanında aşağıdaki gibi bir hata alırız:

main.cpp:29:14: error: use of deleted function ‘std::unique_ptr::unique_ptr(const std::unique_ptr&amp;) [with _Tp = s; _Dp = std::default_delete<s>]’<span id="mce_SELREST_start" style="overflow:hidden;line-height:0;"></span>

Yani ‘deleted’ bir fonksiyon olan copy ctorı çağıramazsın. Peki bir unique_ptr yi bir fonksiyona veya bir sınıfa nasıl transfer edeceğiz, call by value yerine call by reference ile, tekrardan üstteki örnek:

#include <iostream>
#include <memory>

using namespace std;

struct s
{
    int a;
    int c;
    string b;

    s(){cout << "S ctor" << endl;}
    ~s(){cout << "~S dtor" << endl;}
};

void func(unique_ptr<s> &e)
{
    e->a = 99;
}

int main()
{
    unique_ptr<s> uptr(new s);

    cout  << "func begin" << endl;
    func(uptr);
    cout  << "func end" << endl;      

     cout << uptr->a << endl;

}

Yapıtığım tek şey ‘void func(unique_ptr)’ fonksiyon imzasını ‘void func(unique_ptr &)’ ile değiştirmek böylelikle sahiplik değişmeden unique_ptr sınıfını func fonksiyonuna parametrik olarak geçirmiş oluruz.Ayrıca std::move kullanarak da sahipliğini ‘move’ ederek değiştirebiliriz. En nihayetinde auto_ptr de karşılaştığımız bellek(memory) sorunlarından da kurtuluyoruz.

shared_ptr
Her ne kadar unique_ptr ile bellek hatalarını izole etsekte kullanım olarak hala sıkıntılarımız mevcut. Ham (raw) göstericilerdeki özellikleri olabildiğince gerçek hale getirmek akıllı göstericilerin implementasyonuna yön veren amaçlardan biri. Öyleyse bir den fazla pointerın aynı bellek alanına işaret etmesini de gerçeklemek gerekiyor.

Tam bu nokta da shared_ptr devreye giriyor. shared_ptr aslında bir referans sayacı tutuyor. shared_ptr ctor’ında başlangıç değeri olarak ‘new’ ile oluşturulan nesne verildiğinde aslında iki tane bellek tahsisi mevcut. Birincisi direkt zaten ‘new’ ile tahsis edilen nesne ikincisi ise referans sayacını kontrol eden, yöneten nesne(Manager Object). Örneğin sp1 ilk oluşturulan nesnemiz olsun içinde bir de Manager Object oluşturulacak. Diğer oluşturulan shared_ptr nesneleri için bu nesne de bulunan sayaç bir arttırılacak. Tam tersi şekilde Manager object içindeki shared sayacı ve weak sayacı sıfırlandığında ise heap bellek alanından tahsis edilen nesne free edilecek.

 

Yine aynı örnek üzerinden gidersek:

#include <iostream>
#include <memory>

using namespace std;

struct s
{
    int a;
    int c;
    string b;

    s(){cout << "S ctor" << endl;}
    ~s(){cout << "~S dtor" << endl;}
};

void func(shared_ptr<s> e)
{
    e->a = 99;
    cout << "count of shared pointer:" << e.use_count() << endl;
}

int main()
{
    shared_ptr<s> sptr(new s);

    cout  << "func begin" << endl;
    func(sptr);
    cout  << "func end" << endl;      

    cout << sptr->a << endl;

}

Görüldüğü üzere func fonksiyonunu referansla çağırmama rağmen herhangi bir bellek hatasıyla karşılaşmıyorum. Burda Manager object, shared pointer sayacını bir arttırıyor. Func fonksiyonu sonunda ise yine sayacı bir azaltıyor(Argüman olarak yaratılan ‘shared_ptr’ türünden ‘e’ nesnesinin ömrü sona erdiği için). Sonrasında process sorunsuz olarak sonlanıyor.

make_shared: Yukarıda bahsettiğimizi gibi shared pointer oluşturulduğunca 2 kez dinamik bellek tahsisi yapılır. Birisi yönetici nesne için diğeri ise T türü için. Performans açısından bu ikisini aynı anda tahsis etmek maliyetli bir işlemdir. Bu soruna standart kütüphaneden make_shared ile çözüm bulunmuş. make_shared kullanıldığında aynı bir kez bellek tahsisini hem T türü için hem de yönetici nesne için beraber yapar. Yani tek bir blok heapten alınır bu da tasarımı itibarıyla pahalı işlem olan dinamik bellek tahsisi teke düşmüş olur. Yine yukarıdaki örnekten devam edersek:

//shared_ptr<s> sptr(new s);
shared_ptr<s> sptr = make_shared<s>();

şeklinde değiştirmek yeterli olacaktır.

weak_ptr
Raw pointer aracılığıyla oluşturulamaz shared_ptr veya başka bir weak_ptr nin kopyalanması, atanmasıyla yaratılır. shared_ptr gibi çalışır ama nesnenin ömrüne müdahalede bulunmaz. Yönetici nesnede (Manager Object) sayaç sıfır dahi olsa hiçbir şekilde tahsis edilen objenin ömrüne etki de bulunmaz. Sadece okuma hakkında sahiptir. Ownership (sahiplik) sadece shared_ptr dedir. Tipik olarak nesneye erişiminizin gerektiği fakat sayacı artırmanıza gerek olmadığı durumlarda kullanılır. Ayrıca lock() fonksiyonu ile weak_ptr işaret ettiği shared_ptr elde edilebilir.

#include <iostream>
#include <memory>

using namespace std;

struct s
{
    int a;
    int c;
    string b;

    s(){cout << "S ctor" << endl;}
    ~s(){cout << "~S dtor" << endl;}
};

void func(shared_ptr<s> e)
{
    e->a = 99;
    cout << "count of shared pointer:" << e.use_count() << endl;
}

int main()
{
    shared_ptr<s> sptr = make_shared<s>();
    {
        //weak_ptr<s> wptr (new s);   error: main.cpp:29:33: error: no matching function for call to ‘std::weak_ptr<s>::weak_ptr(s*)’
        weak_ptr<s> wptr = sptr;
        cout << "count of weak pointer:" << wptr.use_count() << endl;
    }

    cout  << "func begin" << endl;
    func(sptr);
    cout  << "func end" << endl;      

    cout << sptr->a << endl;

}

enable_shared_from_this : weak_ptr ‘ nin kullanıldığı yerlerden biridir. Boost kütüphanesinin C++ diline kazandırdığı bir diğer özellikdir. Örneğin bir üye fonksiyondan başka bir fonksiyona ‘this’ pointerını geçmek istediğinizde eğer nesne shared_ptr tarafından kontrol ediliyorsa bir diğer shared_ptr objesi yaratmak durumunda kalacaksınız. Bu da yine dangling pointer ‘a sebep olacak. Örneğin:

void printb (struct s *ps)
{
    cout << b << endl;
}

struct s
{
    int a;
    int c;
    string b;

    s(){cout << "S ctor" << endl;}
    ~s(){cout << "~S dtor" << endl;}
    void printB()
    {
        printb(this);

    }
};

Burda görüldüğü üzere aynı bellek alanını iki adet manager object yönettiğinde herhangi birinin sayacı sıfırlandığında gösterdiği obje free edileceğinden yine bir dangling pointer olma durumu mevcut. Bunu engellemek için sınıf içerisinde bir tane weak_ptr barındırarak nesnenin ömrüne etki etmiyoruz. Bunu da türetme yöntemiyle şu şekilde implemente ediyoruz:

void printb (shared_ptr ps);
struct s : public enable_shared_from_this <s>
{
    int a;
    int c;
    string b;

    s(){cout << "S ctor" << endl;}
    ~s(){cout << "~S dtor" << endl;}
    void printB()
    {
        shared_ptr<s> sptr = shared_from_this(); // burda ikinci bir manager object üretilmiyor, sadece shared_ptr counter 1 arttırılıyor.
        printb(sptr);

    }
};

void printb (shared_ptr<s> ps)
{
    cout << ps->b << endl;
}

Her ne kadar smart pointers size daha güvenli bir kullanım sunsa da yine de dikkat edilmesi gereken yerler var. Özellikle unique_ptr, shared_ptr ve weak_ptr şablon sınıflarının kullanımlarının iyi bir şekilde öğrenilmesi ve uygulamayla pratik yapılmasında fayda var. He bir de auto_ptr ‘ yi aklınıza bile getirmeyin 🙂

C++11 – Derleme zamanında tür belirleyiciler (type traits)

Basitçe derleme zamanında tür hakkında bilgi edinmemizi sağlarlar. Kullanımları oldukça basittir:


#include <iostream>
#include <memory>
#include <type_traits>

using namespace std;

class A
{
public:
    ~A(){cout << "~A()" << endl;}
     A& operator=(const A&) { return *this; }
};

int main() 
{    
    cout << boolalpha << endl;
    
    cout << is_object<int>::value << endl;
    cout << is_object<int&>::value << endl;
    cout << is_object<A>::value << endl;
    cout << is_object<A&>::value << endl;
    
    cout << "-----------------------------" << endl;
    
    struct B {};
 
    enum class C {};   
    
    cout << is_class<A>::value << endl;
    cout << is_class<B>::value << endl;
    cout << is_class<C>::value << endl;
    cout << is_class<int>::value << endl;    
    
    cout << "-----------------------------" << endl;
    
    function<void()> f = [](){cout << "is function object move assignable ?" << endl;};

    cout << is_move_assignable<B>::value << endl;
    cout << is_move_assignable<C>::value << endl;
    cout << is_move_assignable<decltype(f)>::value << endl;
    cout << is_move_assignable<A>::value << endl;
 
}

Başka bir kullanım alanları ise şablon (template) fonksiyonları için bir nevi koşul veya kısıtlama getirmek için de kullanılırlar. Tipik olarak std::enable_if ile kullanılırlar, std::enable_if temel olarak SFINAE (Substitution failure is not an error) prensibine dayanır. Kısacası derleyici, şablon fonksiyon overloadlarını yazarken oluşan hatayı görmezden gelir ve diğer fonksiyonları yazmaya devam eder., Örneğin:

#include <iostream>
#include <memory>
#include <type_traits>
using namespace std;

class A
{   
public:
    
    friend ostream &operator<<(ostream &os, const A&) 
    {
        os << "class A" << endl; 
    }
};

template <class T>
typename enable_if<is_integral<T>::value, T>::type
println(T a)
{
    cout << "if T is a integral type: " << a << endl;
};


template <class T>
typename enable_if<is_object<T>::value, T>::type
println(T &a)
{
    cout << "if T is a object type: " << a << endl;
};

int main(int argc, char**argv) 
{
    
    A a;
    
    println(5);
    println(a);
    
    return 0;
}

Örnekte görüldüğü üzere println fonksiyonu iki farklı şekilde şablonu üretiliyor. “enable_if::value, T>::type” ifadesi ile sadece istenen türlerle o fonksiyonun çağrılmasına olanak veriyor. is_object aslında derleme zamanında gelen türün nesne mi olup olmadığını test etmemizi sağlıyor. Böylelikle kullanıma göre derleme zamanında uygun şablon fonksiyonlar yazılacak.

type traitslerin aşağıdaki linkte bir listesini bulabilirsiniz:

http://en.cppreference.com/w/cpp/types

C++11 – std::tuple üzerine

STL’ i kullananlar bilirler genelde iki farklı türden anlamsal birliktelikleri olan değişkenleri tutmak için genelde std::pair kullanılırdı.

pair<int, string> pt (1, "herbibk");
cout << pt.first << "-" << pt.second << endl;

C++11 ile gelen veri yapılarından biri olan std::tuple’ la birlikte bu özellik sınırsız türde nesneyi sınırsız sayıda içermeye kadar gitti. Üstelik derleme zamanında veri yapısındaki nesne sayısını da biliyorsunuz.

Aslında tuple’ ın nasıl implemente edildiğine baktığınızda basitçe C++’ ın iki temel özelliğini kullanılmış; Çoklu Türetme (Multiple Inheritance) ve Değişken parametreli şablonlar (variadic templates).

template <typename T>
struct tuple_leaf
{
T value_;
};

template <typename... Types>
class tuple : tuple_leaf<Types>...
{
};

‘tuple_leaf…’ parametre pack’i (türkçesini daha düşünmedim) ifadesi tuple_leaf, tuple_leaf, tuple_leaf….tuple_leaf şeklinde açılacak böylelikle tuple sınıfı da bunlardan çoklu türetmeyle türetilecek.

Fakat burda şöyle bir sıkıntı var, aynı türde nesneler geldiğinde örneğin T1 ve T3 string olduğunda bu yapı çalışmayacak zaten var olan bir sınıf için ikinci defa bir sınıf yazılmayacak ve derleme hatası verilecektir. STL de bu sıkıntıyı aşmak için std::integer_sequence kullnılmış derleme zamanında indis üretmek için. Böylelikle aynı tür nesneler geldiğin de bile indisleri farklı olduğundan farklı bir sınıftan türetiliyormuş gibi olacak. Biraz zorlama bir trick olmuş .)

Peki daha önceki variadic template fonksiyonlarından bahsettiğimiz yazılarımdaki1 gibi recursive bir tanım yaparsak bu sorunları aşabilir miyiz ?

template <class... Ts> struct tuple {};

template <class T, class... Ts>
struct tuple<T, Ts...> : tuple<Ts...> {
tuple(T t, Ts... ts) : tuple<Ts...>(ts...), tail(t) {}

T tail;
};

Yukarılda görüldüğü gibi rekürsiv olarak her bir tür için bir sınıf diğer kalan türlerden türetilerek oluşturulacak.

Aslında şöyle bir yapı ortaya çıkacak;

struct tuple<T1, T2, T3> : tuple<T2, T3> {
T1 tail;
}

struct tuple<T2, T3> : tuple<T3> {
T2 tail;
}

struct tuple<T3&>: tuple {
T3 tail;
}

struct tuple {
}

Rekürsiv olarak tanımlamak biraz kafa karıştırsa da en kısa yol. Standart kütüphaneye bakıldığında bu işi non-recursive yoldan halletmişler. Tuple implementasyonunu meta programming bakış açınızı güçlendirmek adına inceleyebilirsiniz.

Göz at:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4296.pdf
http://blogs.microsoft.co.il/sasha/2015/01/12/implementing-tuple-part-1/

C++11 – Üye fonksiyonlarda ‘default’ ve ‘delete’ manipülasyonu

C++03 standartlarına göre boş bir sınıf tanımlasanız bile derleyici tarafından bir tane default constructor, copy ctor(constructor) ve atama operatörünü (operator=) sizin için oluşturuyordu.

C++11 standartlarıyla beraber direkt olarak derleyicinin bu tarz davranşlarını manipüle etme şansına hakim oluyorsunuz. C++ programlama dilini işte en çok bu yüzden seviyorum, yazılımcıya olabildiğince fazla hareket alanı sağlıyor.

Tipik bir örnek olarak eğer sınıfınıza herhangi bir ctor tanımladığınızda derleyici sizin için ctor tanımlamayacaktır. Aşağıdaki kodu inceleyelim;


#include <iostream>

using namespace std;

class herbibk
{
public:
    herbibk(int a) {};
};

int main(int argc, char**argv)
{

    herbibk hbk(5); //ok
    herbibk hb; //hata derleyici default ctor oluşturmadığı için:
                      //main.cpp:23:13: error: no matching function for call to ‘herbibk::herbibk()’

    return 0;
}

C++11 ile gelen yeni özellikle beraber derleyiciye kardeşim sen yine bana default ctor üret diyebiliyoruz;

#include <iostream>

 
using namespace std;
 
class herbibk
{
public:
    herbibk(int a) {};
    herbibk()  = default;
};
 
int main(int argc, char**argv) 
{
 
    herbibk hbk(5); //ok    
    herbibk hb; //ok
    

 
    return 0;
}

Görüldüğü üzere artık derlerken hata almıyoruz.

herbibk()  = default;

İfadesini sadece sınıfın tanımına ekleyerek derleyici tarafından oluşturulan default ctor’un her zaman oluşturulmasını sağladık.

Başka bir kullanımı ise, daha önce C++03 zamanıda : ) bir sınıfı kopyalanamaz yapmak için copy ctor’unu ve atama operatörünü private yapılırdı. Şimdi bizim ‘delete’ imiz var hatta operator new’ i de ‘delete’ edebiliriz, Şöyle ki;

#include <iostream>

 
using namespace std;
 
class herbibk
{
public:
    herbibk(int a) {};
    herbibk()  = default;
    herbibk(const herbibk&) = delete;
    herbibk& operator=(const herbibk&) = delete; 
    void* operator new(std::size_t) = delete;
    void* operator new[](std::size_t) = delete; 
};
 
int main(int argc, char**argv) 
{
 
    herbibk hbk(5); //ok    
    herbibk hb(hbk); //hata  derleyiciye copy ctor fonksiyonunu oluşturma demiştik : main.cpp:21:19: error: use of deleted function ‘herbibk::herbibk(const herbibk&)’
    
    hb = hbk; // hata derleyiciye atama operatörü fonksiyonunu oluşturma demiştik 🙂  : main.cpp:23:8: error: use of deleted function ‘herbibk& herbibk::operator=(const herbibk&)’
    
    herbibk *pHbk = new herbibk[4]; // hata main.cpp:25:34: error: use of deleted function ‘static void* herbibk::operator new [](std::size_t)’
    herbibk *pHbk = new herbibk; // hata main.cpp:26:25: error: use of deleted function ‘static void* herbibk::operator new(std::size_t)’

    return 0;
}

‘delete’ anahtar kelimesi otomatik tür dönüşümünü engellemek amacıyla tüm üye fonksiyonlara uygulanabilir, Örneğin;

#include <iostream>

 
using namespace std;
 
class herbibk
{
public:
    herbibk(int a) {};
    herbibk()  = default;
    herbibk(const herbibk&) = delete;
    herbibk& operator=(const herbibk&) = delete;
    void f(int a) {};
    void f(double &) = delete;
};
 
int main(int argc, char**argv) 
{
 
    herbibk hbk(5); //ok    
    herbibk hb;
    
    hb.f(5); // ok sorun yok
    hb.f(5.); // ok sorun yok, çünkü otomatik olarak 5.0 int türüne dönüştürüldü.
    
    return 0;
}

Ayrıca bu kullanımı fonksiyon şablonlarını (function template) kullanarak genelleştirebiliriz; Örneğin f fonksiyonunu integer türü haricindeki hiçbir argümanla çağıramayalım:

#include <iostream>

 
using namespace std;
 
class herbibk
{
public:
    herbibk(int a) {};
    herbibk()  = default;
    herbibk(const herbibk&) = delete;
    herbibk& operator=(const herbibk&) = delete;
    void f(int a) {};
    template<typename T> void f(T) = delete;
};
 
int main(int argc, char**argv) 
{
 
    herbibk hbk(5);  
    herbibk hb;
    
    hb.f(5); // ok sorun yok 
    hb.f(5.00); // hata: main.cpp:26:14: error: use of deleted function ‘void herbibk::f(T) [with T = double]’
    
    return 0;
}

Standart string kütüphanesi fonksiyonlarında geri dönüş değeri olarak “unsigned int”

Birçok yerde denk geldiğim için bununla ilgili bir blog yazısı yazmanın iyi olacağını düşünüyorum. Şimdi aşağıdaki koda bir bakmanızı rica ediyorum, ekrana ne yazılmasını bekliyorsunuz ?

#include    <iostream>
#include    <string>

using namespace std;

int main()
{
    string s("cppturkey");

    unsigned int index = s.find_last_of(".");

    if (index == string::npos)
        cout << "Nokta namevcut" << endl;
    else
        cout << "Nokta mevcut" << endl;

    return 0;

}

Aslında bu sorunun bir cevabı yok, sistemden sisteme değişebilir. string operasyonları geri dönüş değeri “size_t” türündendir. size_t ise standartlarda mevcut sistemin en yüksek pozitif alabileceği değerdir(pozitif -1). Aşağıdaki örneğe göz atabilirsiniz.


#include <iostream>
#include <string>

using namespace std;

int main()
{
unsigned int x = -1;
size_t y = -1;

cout << x << endl;
cout << y << endl;
return 0;

}

 

Türler arasındaki bağlantı C++ standartların aşağıdaki şekilde tanımlanmıştır ve garanti altındadır:

sizeof(char) <= sizeof(short) <= sizeof(int) <= sizeof(long) = sizeof(size_t) <= sizeof(long long)

Dolayısıyla kullandıgınızda sistemde sizeof(unsigned int), sizeof(size_t)'dan farklı olabilir. Küçük olduğu durumlarda integer overflow olduğundan 'index == string::npos' ifadesi doğru olmayacak ve ekrana "Nokta mevcut" yazacaktır.

Garantili yol ise index değişkenini size_t türünden;

size_t index = s.find_last_of(".");

şeklinde tanımlamak olacaktır.

Smart Pointers History – from 03 to 17

Smart pointerların anlatıldıgı C++03 ten C++17 standartlarına kadar nelerin değiştiğinin anlatıldıgı söyleşimize herkes davetlidir.

Smart Pointers – from 03 to 17

Friday, Mar 27, 2015, 3:00 PM

Yıldız Teknopark
İkitelli- Başakşehir Istanbul, TR

2 C++’ers Went

Kat 1 No: 101

Check out this Meetup →

GDB Remote Debug (Windows makinesinden Linux Makinesindeki binariyi debug etme)

This is a document which I prepared at work. That why I needed to prepare it in Turkish. If people show interest I will add translation.

GDB çok faydalı bir tool. Özellikle gdbtui olarak kullanınca bizim vazgeçilmez silahlarımızdan biri oluyor . Fakat diyelimki debug etmek istediğimiz structure: “ntplInfo.u.errorData.errBuffer[0]” gibi birşey. Bu structure ‘in içine bakmak için yazıcağımız gdb komutu o kadar uzun oluyorki, gdb konsolununda pek kullanıcı dostu olmamasından dolayı çalışmak imkansız hale geliyor . Bunun için user interface desteği almamız gerekiyor. Ben aşağıda eclipse araçılığıyla linux binarisini nasıl debug ederiz anlatmaya çalışacağım.

Şimdi adımları tarifi vermeden önce kullanıcağımız malzemelerden bahsedelim .

1- Linux makinesinde gdb ve gdbserver , versiyonlar 7.6 veya üstü olmalıdır.

2 – Windows makinesinde gdb’nin aynı versiyonun kurmalıyız. Bunun için mingw indirmemiz lazım. Yanı sıra derleyebilmemiz için expat isimli projeyi de manuel derlememiz lazım . Bunlardan sonra gdb yi derleyebiliriz .

3 – Eclipse ve eclipse pluginleri . Eclipse ‘in remote connection ile ilglli bütün core pluginlerine sahip olmamız lazım . Pluginler default geliyor zaten çok merak etmeyin .

Şimdi Gelelim Tarife:

1 – Zaten sistem takımımz tarafından GDB ve gdb server linux makinelerine kuruluyor . Siz yinede kontrol edin .

2- GDB yi linuxde default çalıştırın , x86_64-unknown-linux-gnu gibi bir banner görüceksiniz. Bunu note edin ileride kullanıcağız.

3- Mingw ‘yi indirin cvgwin ile denemeyin sorun çıkıyor .

4 -Expat source kodunu inidirin. Ve mingwden indirdiğin folder’A gidip ./configure , make , make install yapın.

5- GDB 7.7 nin source kodunu indirin  .”./gdb-7.4/configure –with-expat –target=x86_64-unknown-linux-gnu –host=i686-pc-mingw32″ ile konfigure edin .Ve sonra make ve make install yapın.

Şimdi gelelim ,manuel testte acaba bu adıma kadar herşeyi yaptıkmı . Ben burada yaklaşık 3-4 saat kaybettim umarım siz bu yazının sayesinde kaybetmezsiniz.

6 – Linux makinesinde dummy bir proje oluşturalım ismide hello olsun . Hello ‘u g++47 -g -o hello hello.cpp diyerek derleyelim.

7 – Linux makinesinde gdbserver ‘ı çalıştıralım : “gdbserver –debug localhost:10000 hello”, ben 10000 portunu seçtim sizin keyfiniz bilir

8 – 10000 portunu açalım yoksa OS reject ediyor (3-4 saat burda kaybettim)

[root@tux ~]# iptables -I INPUT -p tcp –dport 10000 –syn -j ACCEPT
[root@tux ~]# iptables -I INPUT -p tcp –sport 10000 –syn -j ACCEPT

9 – Windows makinesine hello binarisini ve hello.cpp’yi kopyalıyalım.

10 – Mingw ‘den hello ‘nun bulunduğu dizine gidip “./gdb  hello” ile çalıştıralım. Ve sonrada “target remote 172.16.42.244:10000” komutu ile linux makinemizdeki(172.16.42.244) gdbserver ‘a bağlanalım.

image2014-6-4 14-6-56  console’dan testlerimizi yaptık. Ama asıl amaçımız eclipseden bir GUI yardımı ile bunu başarmak.

11  –  Eclipse ‘e geçmeden önce son olaraki global variable ‘lara mingw/bin path ini ekleyelim(sizin mingw niz nerdeyse ona göre ayarlayın). Ve yukardaki adımları windows cmd ‘den deneyin global variable ‘ı doğru set ettiyseniz herşey yukardaki gibi çalışacaktır .

Geldik eclipse kısmına, eclipse bir sürü butonlar ve opsiyonlar koymuş fakat bir çoğu bizim konumuzla alakasız ve kafa karıştırıcı.

12 – Eclipse ‘de projenizi seçin, benim bu örnekte anlataçağım artık hello değil napa_project olacak. run–>debug_configurations penceresini açın. Burda sol menüde “C/C++ attach to application”  opsiyonun seçin diğerleri benim gibi zaman kaybetmenize neden olacaktır.

13 – C/C++ application kısmına debug etmek istediğiniz binariyi şeçin, project olarak debug etmek istediğiniz projeyi şeçin. Benim için ekran çıktısı:

image2014-6-4 20-18-52

14 – Aynı tabde şimdi debugger ‘a gelin, debugger olarak gdbserver şeçin , gdb olarak windows ‘a yeni kurduğumuz gdb binarisini seçin . GDB command file ‘I .gdbinit olarak bırakın .

image2014-6-4 20-21-52

15 – GDBSERVER seçtiğinizde debugger tab’ında  yukarda gözüktüğü gibi Connection sekmesi açılacaktır . host ip yi(linux ip si) ve gdbserver ‘in dinlediği portu(10000) buraya yazın .

image2014-6-4 20-23-11
16 – .gdbinit dosyasının içine aşağıdaki satırları kendinize uygun olarak girin

file test
b main
set solib-absolute-prefix /root/erdemdem/napa_project/
set solib-search-path /lib64

17 – Eğer warning: Could not load shared library symbols for 11 libraries, e.g. /opt/napatech3/lib/libntapi.so.

gibi bir warning alıyorsanız

set solib-search-path /opt/napatech3/lib/ , gibi gdbinit dosyasına eklendi yapın buraya bir kere koymanız gerekecek her gdb çalıştığında her zaman otomatik çalışacaktır.

18 – Linux tarafında gdbserver ayakta ve listening modda olduğunda emin olun ,

19 – Debugı başlatın , gdbinit ‘e b main koyduğumuzdan main ‘de duracaktır .

20 – GUI den break point, next ,continue gibi testleri yapın

Sonuç : unlimited debugging powerrrr

image2014-6-4 20-29-40

SharpEar Multi Channel Acoustic Camera Simulation

SharpEar is a open source “Microphone array” simulation project.
SharpEar simulates a “Microphone Array” and “Room”.
User can add voice, noice, moving voices in to the Room. After User selects a .wav file and a position for this sound in the room; user can trigger beamforming . According to the position of the “Microphone Array” and “Sound Sources” beamforming will color the room. User can choose a “Sound” as “Noice”, “Voice” or even a Moving “Voice”.

Features

  • Room Simulation For Microphone Array
  • Wav files will be read and could be attached any point in the room
  • Sounds could be categorized by User as noice,sound or moving sound.
  • Beamforming and Room Coloring according to beamforming result in each point
  • Speaker Identification
  • Adaptive Beamforming according to speaker identification

Links :

https://sourceforge.net/projects/sharpear/

https://github.com/kerdemdemir/sharpEar

sharpEarOutput

Macro’s vs Variadic Templates – 2

Former article was visited by C++ fans from around the world. Therefore i decided to publish my article in english. Thanks for visiting web site.

More Generic Design
You know templates provide more generic design than function overloading. And now with C++11, you can even design more generic templates than templates, so variadic templates.

I want to write a function that gives me size of all containers in STL(even if they almost all have a size function 🙂 ). You know STL has two main container types:
– Sequence Containers
– Associative Containers

Sequence containers take 2 arguments ValueType and mainly Allocator:

template<class T,
         class Allocator = std::allocator<T>
         > class SeqContainer;

I will try to implement my function-template for sequence containers.

template < template <typename, typename> class ContainerType, typename ValueType, typename Alloc>
size_t sizeOfContainers(ContainerType<ValueType, Alloc> &c)
{
    size_t s = 0;
    for (auto element : c) {
        s++;
    }
    return s;
}

As you see first template parameter “template class ContainerType” is a template class as template parameter. Second and third one are template parameters of first parameter.

But that function template will not work for std::array type. We will look that problem later. Mainly this function satisfies our needs.

#include <vector>
#include <iostream>
#include <memory>
#include <map>
#include <forward_list>
#include <unordered_set>
#include <array>

using namespace std;

template < template <typename, typename> class ContainerType, typename ValueType, typename Alloc>
size_t sizeOfContainers(ContainerType<ValueType, Alloc> &c)
{
    size_t s = 0;
    for (auto element : c) {
        s++;
    }
    return s;
}



int main()
{
    vector<int> v({1, 4, 5, 3, 5, 6});
    forward_list<int> fl ({ 34, 99, 10, 71});
    
    cout << sizeOfContainers(v) << endl;
    cout << sizeOfContainers(fl) << endl;

   
  return 0;
}

Associative containers take 3 or 4 arguments for example:

std::set

template<
    class Key,
    class Compare = std::less<Key>,
    class Allocator = std::allocator<Key>
> class set;

std::map

template<
    class Key,
    class T,
    class Compare = std::less<Key>,
    class Allocator = std::allocator<std::pair<const Key, T> >
> class map;

std::unordered_set

 
template<
    class Key,
    class Hash = std::hash<Key>,
    class KeyEqual = std::equal_to<Key>,
    class Allocator = std::allocator<Key>
> class unordered_set;

Therefore we need two more overload for our function to handle this containers. Let’s try to write them :


template < template <typename, typename, typename> class ContainerType, typename FirstType, typename SecondType, typename AllocType>
size_t sizeOfContainers(ContainerType<FirstType, SecondType,  AllocType> &c)
{
    size_t s = 0;
    for (auto element : c) {
        s++;
    }
    return s;
}

template < template <typename, typename, typename, typename> class ContainerType, typename FirstType, typename SecondType, typename ThirdType, typename AllocType>
size_t sizeOfContainers(ContainerType<FirstType, SecondType, ThirdType, AllocType> &c)
{
    size_t s = 0;
    for (auto element : c) {
        s++;
    }
    return s;
}

As you see for that functions can handle this issue for different type.

But now we have three overloads, can we reduce it to one function with variadic functions ? Lets try it:

template <template <typename, typename...> class ContainerType, typename FirstType, typename... Types> 
size_t sizeOfContainers(ContainerType<FirstType, Types...> &c)
{
    size_t s = 0;
    for (auto element : c) {
        s++;
    }
    return s;
}

Now we can handle all 2,3,4 parameter containers. Except one std::array. std::array takes two parameter:

template< 
    class T, 
    std::size_t N 
> struct array;

Remember, we discussed variadic template function creation at first article. In that example our variadic function will not create function that we need for std::array. Actually second type of std::array is not a type, it is a constant. And it is not allowed in C++, also compile time error. Function overloading take responsibility for us :

template <template <typename, size_t> class ContainerType, typename FirstType, size_t S> 
size_t sizeOfContainers(ContainerType<FirstType, S> &c)
{
    size_t s = 0;
    for (auto element : c) {
        s++;
    }
    return s;
}

That overload will handle our issue. Sometimes we need partial specialization overloads to resolve problems.

At the end of the day score is on the board:

Variadic templates 3 – 1 Function overloading

Full Source Code:

#include <vector>
#include <cstdlib>
#include <iostream>
#include <memory>
#include <map>
#include <forward_list>
#include <unordered_set>
#include <array>
#include <set>
#include <deque>

using namespace std;


template <template <typename, typename...> class ContainerType, typename FirstType, typename... Types> 
size_t sizeOfContainers(ContainerType<FirstType, Types...> &c)
{
    size_t s = 0;
    for (auto element : c) {
        s++;
    }
    return s;
}


template <template <typename, size_t> class ContainerType, typename FirstType, size_t S> 
size_t sizeOfContainers(ContainerType<FirstType, S> &c)
{
    size_t s = 0;
    for (auto element : c) {
        s++;
    }
    return s;
}

template < template <typename, typename> class ContainerType, typename ValueType, typename Alloc>
size_t sizeOfContainers(ContainerType<ValueType, Alloc> &c)
{
    size_t s = 0;
    for (auto element : c) {
        s++;
    }
    return s;
}

template < template <typename, typename, typename> class ContainerType, typename FirstType, typename SecondType, typename Alloc>
size_t sizeOfContainers(ContainerType<FirstType, SecondType,  Alloc> &c)
{
    size_t s = 0;
    for (auto element : c) {
        s++;
    }
    return s;
}

template < template <typename, typename, typename, typename> class ContainerType, typename FirstType, typename SecondType, typename ThirdType, typename Alloc>
size_t sizeOfContainers(ContainerType<FirstType, SecondType, ThirdType, Alloc> &c)
{
    size_t s = 0;
    for (auto element : c) {
        s++;
    }
    return s;
}



int main()
{
    vector<int> v({1,2,3,4,5,6});
    map<int, int> m({ {2,3},  {4, 5}});
    set<int> s({8, 7});
    deque<int> d = {10,20,30};
    forward_list<int> fl ({ 34, 77, 16, 2 });
    array<int,10> a = { 2, 16, 77, 34, 50};
    unordered_set<string> us = {"C++", "UG","ISTANBUL","69736c616d"};
    

    cout << sizeOfContainers(v) << endl;
    cout << sizeOfContainers(m) << endl;
    cout << sizeOfContainers(fl) << endl;
    cout << sizeOfContainers(us) << endl;
    cout << sizeOfContainers(s) << endl;
    cout << sizeOfContainers(a) << endl;
    cout << sizeOfContainers(d) << endl;
    
  return 0;
}