25 Ekim 2017 Çarşamba

Member and Pointer Operators

Giriş
C++'ta kullanılabilen Member and Pointer Operators toplam 7 tane. Bütün metodlar gibi bu operatörler istenirse virtual olabiliyorlar.

1. Subscript veya Bracket operator
a [b] şeklindedir.
Bracket operator tek boyutludur. Çift boyutlu operator [][] şeklinde bir operator yoktur!

Şöyle tanımlanır.
const int & MyClass::operator [] (int i)const {...} //accessor
int & MyClass::operator [] (int i) {...} //mutator
Şöyle kullanırız.
MyClass c;
cout << c[2] << endl;
2. Indirection operator
*a şeklindedir.
Aynı zamana dereference operator olarak ta bilinir.Bu metod lvalue ya da rvalue için tanımlanabilir.
Derefence operator elimizde pointer olmayan nesneye *() şeklinde erişim ile tetiklenir.

Şöyle yaparız. Bu durumda sınıfımıza yazdığımı *operator () metodu tetiklenir.
Person p;
auto v = *p;
Elimizde this olsaydı şöyle yapardık.
std::string str = *(*this);
Örnek
Şöyle yaparız. Burada 3 tane metod var. İlk ikisi lvalue için kullanılır. Sonuncusu ise rvalue için kullanır.
T const& operator *() const& ;
T&       operator *() & ;
T&&      operator *() && ;
lvalue örneklerini anlamak kolay. const lvalue için
const A a = A();
*a;
const olmayan lvalue için
A a;
*a;
const olmayan rvalue için
*A();
Örnek
Derefence operator ve pointer arasındaki farkı görmek için şöyle yaparız.
Person *ptr = new Person;
Person p1 = *ptr;   // does not invoke * operator
string str = *p1 // invokes the overloaded operator as it is called on an object.
3 Address-of operator
&a şeklindedir.

4. Structure dereference operator
a->b şeklindedir.


5.Structure reference operator
a.b şeklindedir. C++'ta bu operator overload edilemez. Açıklaması şöyle
Operator . (dot) could in principle be overloaded using the same technique as used for ->. However, doing so can lead to questions about whether an operation is meant for the object overloading . or an object referred to by .
This problem can be solved in several ways. At the time of standardization, it was not obvious which way would be best
6. Member selected by pointer-to-member b of object pointed to by a operator
a->*b şeklindedir.

7.Member of object a selected by pointer-to-member b operator
a.*b şeklindedir.

23 Ekim 2017 Pazartesi

Variadic Function

Giriş
Variadic Function değişken sayıda parametre alabilen metod anlamına gelir. 

Variadic Function Tarihçesi
Açıklaması şöyle. İlginç bir şekilde C diline girmeden önce C++'ta kullanılmış.
To add to the other answers, the func(arg, ...) syntax first developed in C++, and then was incorporated into the ANSI C standard, sometime between 1984 and 1988.
Variadic Parametreler Nasıl Geçilirler
Bu konuda C++ standardı bir açıklama getirmiyor. Derleyiciler kendi çözümlerini üretmişler.  Cevapta stack kullanılması açıklanmış.

Non-POD Type
non-pod tiplerin - örneğin bir sınıf - variadic parametre olarak geçilmesi tanımsız davranış. Yani yapılmaması gerekir. Açıklaması şöyle
You cannot pass non-POD types to C-style variadic functions
Macrolar
va_list, va_start, va_end,va_arg macroları için  şu satırı dahil ederiz.
#include <stdarg.h>
va_list macrosu
Önce bir metod tanımlarız. Şöyle yaparız.
void setList(int count, ...) {
  ...
}
Metodun içinde parametreleri okumak için bir liste oluştururuz. Şöyle yaparız.
void setList( int count, ... ) {
  va_list numbers;
  va_start (numbers, count);
  ...
  va_end (numbers);
}
va_start macrosu va_list değişkenine yazdığı için va_list memset ile sıfırlamaya gerek yok. Şu kod fazladan iş yapıyor.
va_list args;
memset(&args, 0, sizeof(va_list));

va_start(args, xxx);
...
va_end(args);
va_start macrosu - va_list + int
İmzası şöyle
void va_start(va_list ap, last);
Açıklaması şöyle.
The va_start() macro initializes ap for subsequent use by va_arg() and va_end(), and must be called first.
Açıklaması şöyle.
2 The va_start macro shall be invoked before any access to the unnamed arguments.

3 The va_start macro initializes ap for subsequent use by the va_arg and va_end macros. Neither the va_start nor  va_copy macro shall be invoked to reinitialize ap without an intervening invocation of the va_end macro for the same ap .
Şöyle yaparız.
void setList(int count, ... ) {
  va_list numbers;
  va_start (numbers, count);
  ...
  va_end (numbers);
}
va_start macrosu - va_list + char *
Şöyle yaparız.
void debug(int msglevel, const char *fmt, ...)
{
  va_list va;
  va_start(va, fmt);
  vfprintf(stderr, fmt, va);
  va_end(va);
}
va_end macrosu
Aynı metod için listeyi temizlememiz gerekir. Açıklaması şöyle
Each invocation of va_start() must be matched by a corresponding invocation of va_end() in the same function.
va_arg macrosu - int
Parametreye va_arg ile şöyle erişiriz.
for(int n = 0; n < count; n++)
  int num = va_arg(numbers, int);
Bazı Önemli Variadic Metodlar
printf
printf yazısına taşıdım.

vsnprintf
vsnprintf yazısına taşıdım.

vfprintf
Şu satırı dahil ederiz.
#include <cstdio>
Şöyle yaparız.
void debug(int msglevel, const char *fmt, ...)
{  
  va_list va;
  va_start(va, fmt);
  vfprintf(stderr, fmt, va);
  va_end(va);
}
Array Geçmek
Variadic Function yerine array geçmek daha kolay olabilir.

C++
C++'ta Variadic Function'ların bence yeri yok. Artık kullanılmamalı. st::array, std::vector, std::initializer_list ve hatta variadic template bile kullanılabilir.

20 Ekim 2017 Cuma

std::istream_iterator Sınıfı

Giriş
Bu sınıf  akımdan okuma yapmayı sağlar. Şu satırı dahil etmek gerekir.
#include <iterator>
Genel Kullanım 
1. İki tane istream_iterator oluşturarak veri yapısı doldurulur.
2. İki tane istream_iterator oluşturarak std::copy + inserter benzeri bir algoritma kullanılır.

istreambuf_iterator İle Farkı
istreambuf_iterator whitespace gibi karakterleri atlamaz ve okur. istream_iterator ise okumaz.

Constructor - istream
Belirtilen istream nesnesini okunur.

Örnek - kelimelere ayırmak
Elimizde şöyle bir istringstream olsun
string sentence = "And I feel fine...";
istringstream iss(sentence);
Bu string'i kelimelere şöyle ayırırız.
vector<string> tokens;
copy(istream_iterator<string>(iss),
     istream_iterator<string>(),
     back_inserter(tokens));
Örnek - kelimelere ayırmak
Şöyle yaparız.
stringstream ss (sentence);
for (auto w = istream_iterator<string>(ss) ; w != istream_iterator<string>() ; w++) {
  string &word = *w;
  ...
}
Örnek - double okumak
Elimizde bir ifstream olsun
std::string file_name = ...;
std::ifstream afile (file_name);
Stream'deki her double değeri bir vector'e okumak için şöyle yaparız.
std::vector<double> x;

std::copy(std::istream_iterator<double>(afile), std::istream_iterator<double>(),
  std::back_inserter(x));
Örnek
Elimizde bir ifstream olsun.
std::ifstream afile(somefile);
noskipws(afile);
...
std::vector<uint_8> vOfBytes = { std::istream_iterator<uint8_t>(afile),
                                 std::istream_iterator<uint8_t>() };
istream_iterator >> operatörü ile stream'den karakter okur. >> operatörü default olarak whitespace'i atlar. Atlamasın diye noskipws yapıyoruz. Eğer bunu yapmasaydık dosyayı binary açsak bile okunan byte sayısı ile dosya büyüklüğünün farklı olduğunu görecektik. Aşağıdaki kod parçasında bu fark görülebilir.
std::string filename = ...;

// Get file size
std::ifstream ifs(filename, std::ios::binary | std::ios::ate);
int size = (int)ifs.tellg();
std::cout << "Detected " << filename << " size: " << size << std::endl;

// Load file
ifs.seekg(0, std::ios::beg);
std::istream_iterator<char unsigned> start(ifs), end;
std::vector<char unsigned> v;
v.reserve(size);
v.assign(start, end);

std::cout v.size() << " elements" << std::endl;
Çıktı olarak şunu alırız.
Detected foo_binary.bin size: 2113753
Loaded data from foo_binary.bin, with 2099650 elements