C++17

Zadnja standardizacija jezika C++ dogodila se 2017. godine, a nova se očekuje 2020.

Standard iz 2017. godine je donio niz novosti u jeziku i standardnoj biblioteci (za popis vidjeti wikipediju). Za testiranje novih svojstava jezika koristimo gcc prevodilac verzije 8.2 koji instaliran u /usr/local/gcc-8.2.

Kako bismo mogli koristiti novi prevodilac moramo postaviti neke staze što je napravljeno u datoteci setPath.sh. Datoteku treba učiniti izvršnom i izvršiti:

chmod u+x setPath.sh
./setPath.sh

auto deklaracija varijabli raspakiravanjem

Grupa varijabli se može auto deklarirati i inicijalizirati raspakiravanjem objekta tipa std::pair, std::tuple, struct, std::array itd.

tuple<short,int,long> t1{1,2,3};
auto [s,i,l] = t1;
auto & [sr,ir,lr] = t1;
sr++;
assert(sr == get<0>(t1));

Moguće je primijeniti i na polja fiksne dimenzije:

double point[2]{1.1, 5.3};
    auto & [x,y] = point;
    cout << x << "," << y << endl;

To je naročito zgodno u petljama:

map<std::string, size_t> objekti {
       {"A",  700}, {"B", 1786}, {"C",   24}, {"D",  108},
                                 };

for (const auto &[species, count] : objekti) {
    std::cout << "Broj objekata tipa  " << species
              <<  " = "  << count <<".\n";
}

Isto je moguće sa strukturama s javnim varijablama:

struct Employee{
    string name;
    string role;
    unsigned int salary;

};
// ...

vector<Employee> tve;
ve.push_back({"A.A", "W", 1000});
ve.push_back({"B.B", "E", 10000});
for(auto const  & [ name, role, salary] : ve)
     cout << name << "," << role << "," << salary << endl;

Dedukcija tipa parametriziranih klasa

Paramtri predloška klase mogu se deducirati iz argumenata predanih konstruktoru. Možemo pisati:

pair p{1.2,7};          // deducira pair<double,int>
tuple t{"v", 1u, 2.f};  // deducira tuple<const char *, unsigned int, float>
vector vv(3, 2.0);      // deducira vector<double>

Prije C++-17 smo za to morali koristiti pomoćne funkcije (kao na primjer, make_pair, make_tuple i slično).

U nekim situacijama treba sugerirati način dedukcije parametara predloška i tada se pišu deduction guides.

Redukcija dosega varijable u if i switch naredbama

Naredbe if i switch mogu sadržavati u sebi inicijalizaciju varijable. Doseg te varijable je cijela if odnosno switch naredba.

Primjer: if naredba.

set<char>  sc{'a','b','c'};
char c{'c'};
if (auto it = sc.find(c); it != sc.end()) {
       cout << "Znak " << c << " je u skupu na poziciji.\n";
} else {
       // it = sc.end()
       cout << "Znak " << c << " nije u skupu.\n";
}
// it nije više definiran

Primjer: switch naredba.

switch (char c = getchar(); c) {
   case 'a': putchar('.');  break;
   case 's': putchar('-');  break;
   default: putchar('+');
}

Posebno je korisno u ovakvom kontekstu gdje destrukcija objekta ima dodatne efekte.

shared_ptr<int>  sp(new int{5});
weak_ptr<int> wp = sp;
 // ..
if (auto sp1 = wp.lock(); sp1 != nullptr) {
      // sp1 i sp još postoje
} else {
     // sp1 dohvatljiv, ali je nullptr; sp ne postoji
}
// sp1 nije dohvatljiv

Filesystem

C++17 nudi metode za rad sa datotečnim sustavom koje su neovisne o operacijskom sustavu na kojem se program izvršava.

Klase i funkcije za rad sa datotečnim sustavom nalaze se u imeniku std::filesystem u zaglavlju <filesystem>.

Ako koristimo filesystem onda pri pozivu prevodioca moramo proslijediti opciju za povezivanje s filesystem bibliotekom. Kod gcc prevodioca to je -lstdc++fs, a za clang to je -lc++fs.

Klasa std::filesystem::path

Konstruktor kreira stazu iz stringa. Razumije relativne i apsolutne staze.

Metode članice i globalne funkcije:

  • operator ispisa na izlazni stream (ispis u navodnicima);

  • metode članice c_str() i string() koje vraćaju stazu u obliku stringa;

  • metoda članica filename() daje samo ime datoteke (bez staze do datoteke);

  • metoda članica stem() daje ime datoteke bez ekstenzije;

  • metoda članica parent_path() daje stazu do datoteke;

  • globalna funkcija path canonical(path) koja stazu dovodi u kanonsku formu;

  • globalna metoda exists() ispituje da li staza (direktorij, datoteka) postoji u datotečnom sustavu.

  • globalni operator / je preopterećen za konkatenaciju staza;

  • globalna metoda current_path() daje stazu do radnog direktorija.

Primjer:

using namespace std;
using namespace filesystem;

path p1 =current_path() / "structured_bindings.cpp";
cout << p1 << endl;

// Funkcije članice klase path
path filename = p1.filename();
path stem = p1.stem();
path pp = p1.parent_path();
path rp = p1.relative_path();

cout << "p1 = " << p1 << endl;
cout << "p1.filename() = " << filename << endl;
cout << "p1.stem() = " << stem << endl;
cout << "p1.parent_path() = " << pp << endl;
cout << "p1.relative_path() = " << rp << endl;

std::filesystem::directory_iterator

Za iteriranje kroz direktorij koristimo std::filesystem::directory_iterator. End-terator se dobiva pomoću dodijeljenog konstruktora. Dereferenciranjem iteratora dobivamo objekt tipa std::filesystem::directory_entry.

for(auto entry : directory_iterator{dir}){
    cout << entry.path().filename().string() << " ";

   if(entry.is_directory())
         { cout << " je direktorij\n"; }
   else if (entry.is_regular_file())
         { cout << " je datoteka.\n "; }
}

std::filesystem::directory_entry ima niz metoda za ispitivanje datoteke/direktorija: exists, is_block_file, is_character_file, is_directory, is_fifo, is_other, is_regular_file, is_socket, is_symlink, file_size, hard_link_count, last_write_time, status.

Pomoću metode status() dobivamo std::filesystem::file_status objekt koji ima metode type() i permissions(). Metoda permissions() vraća std::filesystem::perms objekt koji predstavlja dozvole pristupa datoteci/direktoriju. Radi se o cjelobrojnom tipu na kojem su definirani samo operatori bit-po-bit te konstante.

Ova petlja ispisuje dozvole pristupa kao i naredba ls -l.

for(auto entry : directory_iterator{dir}){
    auto entry_status = entry.status();
    auto eperms = entry_status.permissions();

    if( (eperms & perms::owner_read) != perms::none )  cout << "r";
    else                   cout << "-";
    if( ( eperms & perms::owner_write) != perms::none ) cout << "w";
    else                   cout << "-";
    if( (eperms & perms::owner_exec) != perms::none )  cout << "x";
    else                   cout << "-";
    if( (eperms & perms::group_read) != perms::none )  cout << "r";
    else                   cout << "-";
    if( (eperms & perms::group_write) != perms::none ) cout << "w";
    else                   cout << "-";
    if( (eperms & perms::group_exec) != perms::none )  cout << "x";
    else                   cout << "-";
    if( (eperms & perms::others_read) != perms::none )  cout << "r";
    else                   cout << "-";
    if( (eperms & perms::others_write) != perms::none ) cout << "w";
    else                   cout << "-";
    if( (eperms & perms::others_exec) != perms::none )  cout << "x ";
    else                   cout << "- ";

    cout << entry.path().filename().string() << "\n";
}