19 Ekim 2017 Perşembe

std::chrono::system_clock

Giriş
Şu satır dahil edilir.
#include <chrono>
tanımlama
Chrono için eski derleyiciler ile typdef kullanılır.
typedef std::chrono::system_clock Clock;
Yeni derleyiciler ile using kullanılır.
using Clock = std::chrono::system_clock;
from_time_t metodu
time_point döner.

Örnek
Şöyle yaparız
time_t t = ...;
system_clock::time_point tp = std::chrono::system_clock::from_time_t (t);
Örnek
Şöyle yaparız.
std::tm tm =...;
auto time_point = std::chrono::system_clock::from_time_t(std::mktime(&tm));

now metodu
Bir time_point döndürür. Windows'ta now metodu GetSystemTimeAsFile metodunu kullanır.

time_since_epoch
Bu metod sadece system_clock sınıfında var. Diğer saatlerde yok. Epoch'tan beri geçen süreyi saatin ölçüm birimi cinsinden verir. Saatin birimi nanosaniye de olabilir, milisaniye de. Örnekte epoch'tan beri geçen süre milisaniye ye çevriliyor.
#include <iostream>
#include <chrono>
#include <ctime>

using namespace std::chrono;

int main()
{
  high_resolution_clock::time_point p = high_resolution_clock::now();

  milliseconds ms = duration_cast<milliseconds>(p.time_since_epoch());
  
}
to_time_t metodu
Bu metod static'tir yani state tutmaz sadece verilen değeri time_t yapısına çevirir. Metod sadece system_clock sınıfında var ve amacı C API'si ile beraber çalışabilmeyi sağlamak. Epoch'tan beri geçen süreyi saniye cinsinden verir.
#include <iostream>
#include <chrono>
#include <ctime>

using namespace std::chrono;

int main()
{
  system_clock::time_point p = system_clock::now();

  std::time_t t = system_clock::to_time_t(p);
  std::cout << std::ctime(&t) << std::endl; //Tue Sep 27 14:21:13 2011
}
Saat ve dakika bilgisine kadar sistem saatini yazdırmak için strftime kullanırız. Şöyle yaparız.
void print_date(std::chrono::time_point<std::chrono::system_clock> tp){
  time_t raw_time = system_clock::to_time_t(tp);
  tm* time_info = localtime(&raw_time);

  char date[20] = {};
  strftime(date, 20, "%Y-%m-%d %H:%M", time_info);

  cout << date;
}
Milisaniye için şöyle yaparız.
std::string TimePointToISOString(const time_point<system_clock>& time)
{
  std::time_t rawTime = std::chrono::system_clock::to_time_t(time);
  struct tm timeinfo;
  localtime_s(&timeinfo, &rawTime);

  milliseconds ms = duration_cast<milliseconds>(time.time_since_epoch());
  std::size_t frac_seconds = ms.count() % 1000;

  char buffer[32];
  std::strftime(buffer, 32, "%Y-%m-%dT%H:%M:%SZ", &timeinfo);
  std::string bufferStr(buffer);
  std::stringstream ss;
  ss << bufferStr.substr(0, 19) << "." << std::setw(3) 
     << std::setfill ('0') << frac_seconds << bufferStr.substr(20);

  return ss.str();
}
Aynı şey için std::put_time da kullanılabilir.

Yuvarlama - Rounding

Giriş
Yuvarlama metodları C++98'te yoktu. C++03 çıktığında C metodlarına eklenmişti ancak C++03 halen altta C90 metodlarını kullandığı için resmi olarak bu sürümde de round metodları yoktu.

C++11 altta artık nihayet C99 metodlarını kullandığı için bu sürümde nihayet round metodlarını kullanabiliyoruz.

Kendi Yuvarlama Metodumuz

1. En Yakın Sayıyya Yuvarlama
Birçok kaynakta kendi yuvarlama metodumuzu yazmak istersek şöyle bir şey yapmamız öneriliyor.
double round(double d)
{
  return floor(d + 0.5);
}
0.5 için = 1 verir
0.4 için = 0 verir
0.49999999999999994 = 0 vermesi gerekiyor ancak 1 verir!

Dolayısıyla kendi metodumuzu yazmak kolay değil.

2. Bir Küçük Çift Sayıya Yuvarlama
İstenen şey şöyledir.
f(8)=8
f(9)=8
Çözüm
Şöyle yaparız
x = x/2*2;
Çözüm
Şöyle yaparız. Yani LSB biti 0 yaparız.
x & (~1u)
boost
Eğer kullandığımız derleyicide yuvarlama metodları yoksa ve kendimiz de yazmak istemiyorsak boost kullanabiliriz. round(), iround(), lround() metodları var.
#include <boost/math/special_functions/round.hpp>

double a = boost::math::round(1.5); // Yields 2.0
int b = boost::math::iround(1.5); // Yields 2 as an integer
Örnek
#include <boost/math/special_functions/round.hpp>
using boost::math::lround;
cout << lround(0.0) << endl;
C++11 
std::round(), std::lround() ve std::llround() metodları var.
std::round floating point alır ve floating point döner.
std::lround floating point alır ancak long döner
std::lround floating point alır ancak long long döner



17 Ekim 2017 Salı

makefile ve target

Giriş
Sözdizimi olarak şöyledir.
target: dependency1 dependency2 etc
    command to build target
Bir başka gösterim ile şöyledir.
target: dependencies
        actions
target ismi ve komut çıktısı aynı olmalıdır.
Elimizde şöyle bir target olsun.
program: client.o
    gcc client.o -o Client
Bu kullanım şekli biraz tuhaf çünkü program target'ı program isimli bir dosya arar ve bulamaz. Çünkü komutun çıktısı Client isimli bir dosyadır. Dolayısıyla Client her seferinde baştan derlenir. Doğru kullanım şöyledir.
Client: client.o
    gcc client.o -o Client
target ve dependency
make programlama dilinden bağımsızdır denilse de alında C++ için bazı built-in kurallar vardır.
Örnek
Elimizde şöyle bir makefile olsun. test target'ı test.o'ya bağlıdır. test.o target'ı ise test.c'ye bağlıdır. test.c değişince yeniden derkenir.
test: test.o
        cc -o test test.o 
test.o: test.c
        cc -c test.c
clean:
        rm test.o
Örnek
Mesela
all:    main.o game.o zombie.o
        g++ $(FLAGS)  game.o main.o zombie.o -o main $(CXXFLAGS)
şeklindeki bir komut .o ile biten dependency'lerin aslında .cpp dosyası olduğunu bilir. Dolayısıyla g++ komutu sadece belirtilen cpp dosyası değişmiş ise çalışır.

Ancak c,cpp dosyaları .h dosyalarına bağımlıdırlar. Bu durumda bazı .c dosyaları için kullandıkları .h dosyalarını da belirtmek  gerekir:
all: main            # all depends on the program
main: main.o foo.o   # main depends on its objects
    gcc main.o foo.o -o main    # create the program by linking the object files
foo.o: foo.c foo.h   # the foo object depends on its .c and .h files
                     # the main object implicitly depends on its .c file

16 Ekim 2017 Pazartesi

STL Veri Yapısı - Vector Eleman Ekleme Metodları

emplace
Bu metod C++11 ile geldi. Aynı emplace_back() gibidir. Tek fark olarak yeni nesne en sona değil verilen iterator'den sonra eklenir.
struct Foo
{
  Foo(int n, double x);
};

std::vector<Foo> v;
v.emplace(someIterator, 42, 3.1416);
emplace_back metodu
Bu metod C++11 ile geldi. emplace_back() metodu verilen parametreleri alarak, vector'ün kendi içinde bir nesne oluşturur. Yani nesnenin constructor metodunu kullanarak aşağıdaki gibi kod yazmaya gerek kalmaz.
v.push_back(Foo(42, 3.1416));
Bunun yerine emplace_back() kullanılır. Elimizde Foo vector'ü olsun. Bu vector'e emplace_back ile şöyle ekleme yaparız.
struct Foo
{
  Foo(int n, double x);
};

std::vector<Foo> v;
v.emplace_back(42, 3.1416);
std::string için şöyle yaparız.
v.emplace_back("some_string");
emplace_back boş çağrılabilir
Eğer nesnemiz parametre almıyorsa sadece emplace_back()'i çağırmamız yeterli.
v.emplace_back();
emplace_back lvalue için çağrılabilir
Şöyle yaparız.
std::string str("some_string");
v.emplace_back(std::move(str));
emplace_back move constructor gerektirir
Açıklaması şöyle
In order to emplace into a vector, the element type must be MoveInsertable and MoveAssignable.
Emplace metodlarının çalışabilmesi için nesnenin move constructor metodunun olması gerekir. Eğer move constructor sağlamıyorsak veya move constructor üretilmesi engelleniyorsa derleyici hata verir.
Örnek
Aşağıdaki kod parçası derlenmez çünkü copy constructor delete edilse bile sağlanıyor ve move constructor'ın üretilmesi engelleniyor.
class MyType {
public:
    MyType(std::array<double, 6> a) {}
    MyType(const MyType& that) = delete;
};

int main() {
    std::vector<MyType> v;
    std::array<double, 6> a = {1,2,3,4,5,6};
    v.emplace_back(a);
}
Dolayısıyla şöyle bir move constructor sağlamamız gerekir.
MyType(MyType &&a) {/*code*/} //move constructor
Örnek
Aşağıdaki kod parçası derlenmez çünkü copy constructor sağlanmış ancak const değil! Derlenmesi için copy constructor const yapılır veya yapılmak istenmezse move constructor tanımlanır.
struct Vertex
{
  Vertex(float pos) { }
  Vertex(Vertex& other) { }
};

std::vector<Vertex> arrayOfVertices;
arrayOfVertices.emplace_back(7.f);
emplace_back'in kendi elemanı ile çağrılması
Elimimizde şöyle bir kod olsun. Bu kod sorunsuz çalışır.
std::vector<int> nums{21, 22, 23, 24};
nums.emplace_back(nums[0]);
nums.emplace_back(nums[1]);

for (auto n : nums) {
    std::cout << n << std::endl;
}
Kendi elemanımız ile kullandığımız için eğer vector resize olursa elemanda bozulmalar olabilir diye düşünebiliriz. Ancak standart bu durum için şöyle bir çalışma kuralı getiriyor.
1. Önce yeni bellek ayrılır
2. emplace_back yeni bellek alanı üzerinde gerçekleşir.
3. Mevcut elemanlar yeni bellek alanına taşınır.
4. Eski bellek silinir.

insert metodu
Açıklaması şöyle. Metod vector'ün boyunu büyütür. 
The vector is extended by inserting new elements before the element at the specified position, effectively increasing the container size by the number of elements inserted.
This causes an automatic reallocation of the allocated storage space if -and only if- the new vector size surpasses the current vector capacity. 
insert metodu vector veri yapısının vermiş olduğu tüm iterator ve reference'ları geçersiz hala getirir.
Metodun C++98 imzası şöyledir.
void insert (iterator position, size_type n, const value_type& val);
C++11 ile imzası ise şöyle
iterator insert(const_iterator position, const T& x);
iterator insert(const_iterator position, T&& x);
Iterator invalidation rules yazısı durumu özetliyor.
all iterators and references before the point of insertion are unaffected, unless the new container size is greater than the previous capacity (in which case all iterators and references are invalidated)
push_back metodu
Yeni C++11 ile push_back metodları iki imzaya sahip oldular. Bunlar
vector<T>::push_back(const T&);
ve move işlemi ile çalışan
vector<T>::push_back(T&&);
metodları.

move ile çalışan metodun için şuna benzer.
void push_back( Object && x )
{
    if( theSize == theCapacity )
        reserve( 2 * theCapacity + 1 );
    objects[ theSize++ ] = std::move( x );
}
Bu metodun iki tane önemli özelliği var. Birinci özelliği metoda geçilen parametrenin kopyasını alınır.
Adds a new element at the end of the vector, after its current last element. The content of val is copied (or moved) to the new element.
İkinci özelliği ise metodun kullanılmakta olan iterator'leri gerçersiz hale (invalidate) getirmesi.
Burada ilginç bir soru var. Vektör'ün içindeki bir elemanı tekrar push_back() ile eklersek ne olur diye soruyor.

exception
Açıklaması şöyle
If an exception is thrown (which can be due to Allocator::allocate() or element copy/move constructor/assignment), this function has no effect (strong exception guarantee).
Açıklaması şöyle
So in case of failure, the last push_back which caused the exception will be rolled back, but everything else will be fine: your vector will contain all of the previously pushed elements, and will be in a consistent state.
Elimizde şöyle bir kod olsun. Eğer push_back exception atarsa v exception fırlatan en son push_back işleminden önceki halinde kalır.
std::vector<int> v;
try {
  for(unsigned int i = 0; i < desiredSize; ++i)
    v.push_back(i);
}
catch (const std::bad_alloc&) {
  cerr << "Out of memory! v.size() = " << v.size() << endl;
}