С++ в относительно новых стандартах

Вопросы написания собственного программного кода (на любых языках)

Модератор: Olej

Аватара пользователя
Olej
Писатель
Сообщения: 14939
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

С++ в относительно новых стандартах

Непрочитанное сообщение Olej » 05 дек 2020, 23:34

Почему такое хитрое название темы?

Потому что:
- меня эти конструкции интересуют в практическом применении, в проекте...
- интересуют возможности C++11, которые расширены и поправлены (не существенно) C++14 ... а вот C++17 мне "до фени", поскольку по моим представлениям это просто адаптация C++ под нужды живущих в мире Windows.

Интересует, главным образом, новые выразительные средства из области STL, функторов, алгоритмов, лямбда-функций etc,

P.S. Т.е. эта тема, в отличие от всего остального форума - заметки (этюды) по мотивам моей текущей практической работы в весьма крупном разработческом (и сопроводительном - дополнениями) проекте ... заметки по конкретным задачам, которые возникнут в ходе этой работы. :!: :idea:

Множество коротких примеров я уже использовал в своём длинном :lol: тексте: Начала STL и контейнеры C++ ... особенно применительно не к контейнерам, а к классическим регулярным массивам C (это писалось в годы 2015-16, когда ещё про C++17 никто, слава Богу, и не слышал).

P.S. Я даже прикреплю, пожалуй, этот текст (в самой свежей и полной редакции) сюда, чтобы он у меня был под рукой.
Вложения
AllSTL_14a.odt
(313.7 КБ) 32 скачивания

Аватара пользователя
Olej
Писатель
Сообщения: 14939
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: С++ в относительно новых стандартах

Непрочитанное сообщение Olej » 05 дек 2020, 23:42

Olej писал(а):
05 дек 2020, 23:34
применительно не к контейнерам, а к классическим регулярным массивам C
Реальная подзадача: определить "принтебельнсть" ASCII символьной строки (желательно самым коротким способом), в смысле POSIX функции isprint().
Тестовая задача:

Код: Выделить всё

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

const char* stest[] = {
   "asdf0987\t",
   "zf""\xAA""bc0de",
   "1234567890",
   "#$@__12345",
   "abc""\xFF",
   "ZXFfxz",
   "строка"
};

int main( int argc, char *argv[] ) {
   for (int n = 0; n < (int)(sizeof(stest) / sizeof(stest[0])); n++) {
      const char *str = stest[n];
      const int len = strlen(stest[n]);
      cout << "<" << str << "> "
           << " | " << (std::all_of(str, str + len, [](char p)->bool{ return ( isprint( p ) != 0 ); }) ? "true" : "false" )
           << endl;
   }
}
Выполнение:

Код: Выделить всё

olej@nvidia:~/2020_WORK/Zodiac_Systems/OWN-DRAFT-TESTs$ g++ printable.cc -o printable

olej@nvidia:~/2020_WORK/Zodiac_Systems/OWN-DRAFT-TESTs$ 
olej@nvidia:~/2020_WORK/Zodiac_Systems/OWN-DRAFT-TESTs$ ./printable 
<asdf0987	>  | false
<zf�bc0de>  | false
<1234567890>  | true
<#$@__12345>  | true
<abc�>  | false
<ZXFfxz>  | true
<строка>  | false
Вложения
printable.cc
(872 байт) 28 скачиваний

Аватара пользователя
Olej
Писатель
Сообщения: 14939
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: С++ в относительно новых стандартах

Непрочитанное сообщение Olej » 05 дек 2020, 23:54

Olej писал(а):
05 дек 2020, 23:42
желательно самым коротким способом
Очень интересная статья про лямда-функции, особенно в части автоматического выведения типов C++14: Лямбда-выражения (анонимные функции) в С++
Обновл. 8 Сен 2020
Мы можем улучшить читабельность кода следующим образом:

Код: Выделить всё

// Хорошо: Мы можем хранить лямбду в именованной переменной и передавать её в функцию в качестве параметра
auto isEven{
  [](int i)
  {
    return ((i % 2) == 0);
  }
};
:lol:

Аватара пользователя
Olej
Писатель
Сообщения: 14939
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: С++ в относительно новых стандартах

Непрочитанное сообщение Olej » 14 дек 2020, 21:31

Вопрос: что дают sizeof() относительно описаний типов/классов, и имплементированных объектов этих классов?
Ничего военного в вопросе нет ... но он возникает (и возник в реальном проекте) при сериализации объектов, когда объект нужно передать через среду передачи (сеть и т.д.) в потоке, но на то конце восстановить в неизменном виде.

Код: Выделить всё

olej@nvidia:~/2020_WORK/Zodiac_Systems/OWN-DRAFT-TESTs$ cat sizeof.cc 
#include <iostream>
#include <cstring>
#include <vector>

#include <algorithm>
using namespace std;

struct xxxx
{
    const static uint16_t p1;
    static int   p2, p3;
    uint8_t data[ 10 ];
};


template <class T> class TClass
{
    T    t1, t2;
};

struct zz
{
    uint8_t d1, d2, d3, d4;
};

struct zzzz
{
    uint8_t data[ 10 ];
};

struct nnnn
{
    static long int n;
    vector<int> data;
    nnnn( void )
    {
	data.resize( n );
    }
};
long int nnnn::n = 3;

int main( int argc, char *argv[] ) {
    xxxx yyyy;
    string s;
    TClass<zzzz> uuuu;
    
    cout << sizeof( xxxx ) << endl 
         << sizeof( yyyy ) << endl
         << sizeof( TClass<zzzz> ) << endl 
         << sizeof( uuuu ) << endl
         << sizeof( s ) << endl
         << sizeof( TClass<string> ) << endl
	 << sizeof( nnnn ) << endl;
    nnnn   N3;
    cout << sizeof( N3 ) << endl;
    nnnn::n = 300;
    nnnn   N300;
    cout << sizeof( N300 ) << endl;
}
Тут, как раз, нет ничего неожиданного...

Код: Выделить всё

olej@nvidia:~/2020_WORK/Zodiac_Systems/OWN-DRAFT-TESTs$ g++ sizeof.cc -o sizeof

olej@nvidia:~/2020_WORK/Zodiac_Systems/OWN-DRAFT-TESTs$ ./sizeof 
10
10
20
20
32
64
24
24
24
Но такие неожиданности попадаются ... просто пока я их не могу сформулировать в локальных примерах.
Если у кого найдётся нетривиальный пример при сериализации - давай его сюда! :lol:
Вложения
sizeof.cc
(947 байт) 30 скачиваний

Аватара пользователя
Olej
Писатель
Сообщения: 14939
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: С++ в относительно новых стандартах

Непрочитанное сообщение Olej » 18 дек 2020, 23:00

Ещё возникла подобная задача: в последовательности элементов (вектор, массив в стиле C, любой контейнер, ...) проверить, что некоторый диапазон последовательных элементов все попадают под какое-то условие (чётные, нечётные, положительные, отрицательные ... простые числа, литеры русского алфавита ... всё что угодно!).

Код: Выделить всё

#include <iostream>     // std::cout
#include <iterator>     // std::next std::next
#include <algorithm>    // std::all_of 
#include <array>        // std::array

using namespace std;

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

   array<int,8> arr1 = {3,5,7,11,13,17,19,23};
   if ( all_of(arr1.begin(), arr1.end(), [](int i){return i%2;}) )
      cout << "All the elements are odd numbers." << endl;

   array<int,12> arr2 = {1,3,5,2,2,4,6,8,10,12,10,8};
   if (all_of(next(arr2.begin(), 3), arr2.end(), [](int i)->bool{return !(i%2);}))
      cout << "The elements begin from 4 are even numbers.\n";

   int arr3[] = {1,3,5,2,2,4,6,8,10,12,10,8};
   if (all_of(arr3 + 3, arr3 + sizeof(arr3) / sizeof(arr3[0]) - 1, [](int i)->bool{return !( i%2 );}))
      cout << "The elements begin from 4 are even numbers.\n";

   array<int,12> arr4 = {1,3,5,2,2,4,6,8,10,11,9,7};
   if (all_of(next(arr4.begin(), 3), prev(arr2.end(), 4), [](int i)->bool{return !(i%2);}))
      cout << "All elements in diapason 3...8 are even numbers.\n";

}

Код: Выделить всё

olej@nvidia:~/2020_WORK/Zodiac_Systems/OWN-DRAFT-TESTs$ g++ nechet.cc -o nechet

olej@nvidia:~/2020_WORK/Zodiac_Systems/OWN-DRAFT-TESTs$ ./nechet 
All the elements are odd numbers.
The elements begin from 4 are even numbers.
The elements begin from 4 are even numbers.
All elements in range 3...8 are even numbers.
P.S. почему next() и prev() а не +3 и -4? потому что это будет работать с любыми контейнерами, а не только с векторами.
Вложения
nechet.cc
(1.18 КБ) 28 скачиваний

Аватара пользователя
Olej
Писатель
Сообщения: 14939
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: С++ в относительно новых стандартах

Непрочитанное сообщение Olej » 20 дек 2020, 03:28

Хронометраж высокой точности (он мне скоро сильно понадобится для сравнения производительности).
Можно, конечно, использовать API классического C, с использованием аппаратных штучек-дрючек (типа команды RDTSC) - хронологическое время с точностью до наносекунд :-o

Но с C++ за годы наработано много собственной специфики...
Используем std::chrono::high_resolution_clock:

Код: Выделить всё

// high_resolution_clock example
#include <iostream>
#include <ctime>
#include <ratio>
#include <chrono>

int main ()
{
  using namespace std::chrono;

  high_resolution_clock::time_point t1 = high_resolution_clock::now();

  std::cout << "printing out 1000 stars...\n";
  for (int i=0; i<1000; ++i) std::cout << "*";
  std::cout << std::endl;

  high_resolution_clock::time_point t2 = high_resolution_clock::now();

  duration<double> time_span = duration_cast<duration<double>>(t2 - t1);

  std::cout << "It took me " << time_span.count() << " seconds.";
  std::cout << std::endl;

  return 0;
}

Аватара пользователя
Olej
Писатель
Сообщения: 14939
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: С++ в относительно новых стандартах

Непрочитанное сообщение Olej » 21 дек 2020, 02:32

Olej писал(а):
20 дек 2020, 03:28
он мне скоро сильно понадобится для сравнения производительности
Как и говорил :lol: ...
Смотрю что там на счёт move семантики в C++11 ...
Углубляемся в C++: move семантика и rvalue
Урок №192. Функция std::move()
Меня интересует скоростные разницы при передаче и возврате строк (std::string) в/из функции.
Пока так:

Код: Выделить всё

#include <iostream>     // std::cout
#include <chrono>       // high_resolution_clock example

using namespace std;
using namespace std::chrono;

string fun1(string s)
{
  // do something with s .... 
  return s;
} 

string fun2(string& s)
{
  // do something with s .... 
  return s;
} 

string fun3(string&& s)
{
  // do something with s .... 
  return move( s );
} 

string fun4(string& s)
{
  // do something with s .... 
  return move( s );
} 

int main( int argc, char *argv[] ) {
  int len = 100000;                   // длина строки
  if( argc > 1 && atoi( argv[ 1 ] ) != 0 )
    len = atoi( argv[ 1 ] );
  int rep = 100000;                   // число повторений
  if( argc > 2 && atoi( argv[ 2 ] ) != 0 )
    rep = atoi( argv[ 2 ] );
  string etalon = string( len, 'm' ), dublic;
  high_resolution_clock::time_point t1, t2;
  duration<double> time_span;

  t1 = high_resolution_clock::now();
  for (int i = 0; i < rep; i++)
    dublic = fun1(etalon);
  t2 = high_resolution_clock::now();
  time_span = duration_cast<duration<double>>(t2 - t1);
  cout << "время 1 = " << time_span.count() << " секунд "
       << ( dublic == string( len, 'm' ) ? "+" : "-" ) << endl;

  t1 = high_resolution_clock::now();
  for (int i = 0; i < rep; i++)
    dublic = fun2(etalon);
  t2 = high_resolution_clock::now();
  time_span = duration_cast<duration<double>>(t2 - t1);
  cout << "время 2 = " << time_span.count() << " секунд "
       << ( dublic == string( len, 'm' ) ? "+" : "-" ) << endl;

  t1 = high_resolution_clock::now();
  for (int i = 0; i < rep; i++)
    etalon = fun3(move(etalon));
  t2 = high_resolution_clock::now();
  time_span = duration_cast<duration<double>>(t2 - t1);
  cout << "время 3 = " << time_span.count() << " секунд " 
       << ( etalon == string( len, 'm' ) ? "+" : "-" ) << endl;

  t1 = high_resolution_clock::now();
  for (int i = 0; i < rep; i++)
    etalon = fun4(etalon);
  t2 = high_resolution_clock::now();
  time_span = duration_cast<duration<double>>(t2 - t1);
  cout << "время 4 = " << time_span.count() << " секунд "
       << ( etalon == string( len, 'm' ) ? "+" : "-" ) << endl;
}

Код: Выделить всё

olej@nvidia:~/2020_WORK/Zodiac_Systems/OWN-DRAFT-TESTs$ g++ rvalue.cc -o rvalue

Код: Выделить всё

olej@nvidia:~/2020_WORK/Zodiac_Systems/OWN-DRAFT-TESTs$ ./rvalue 
время 1 = 0.571068 секунд +
время 2 = 0.569043 секунд +
время 3 = 0.00246565 секунд +
время 4 = 0.00223859 секунд +
А вот дальше начинаются совершенно разительные вещи, которые ещё требуют осмысления:

Код: Выделить всё

olej@nvidia:~/2020_WORK/Zodiac_Systems/OWN-DRAFT-TESTs$ ./rvalue 1000000 50000
время 1 = 4.38805 секунд +
время 2 = 4.38243 секунд +
время 3 = 0.000819742 секунд +
время 4 = 0.000688928 секунд +

olej@nvidia:~/2020_WORK/Zodiac_Systems/OWN-DRAFT-TESTs$ ./rvalue 10000000 5000
время 1 = 11.2338 секунд +
время 2 = 11.5303 секунд +
время 3 = 8.4928e-05 секунд +
время 4 = 7.1658e-05 секунд +

olej@nvidia:~/2020_WORK/Zodiac_Systems/OWN-DRAFT-TESTs$ ./rvalue 100000000 100
время 1 = 7.75443 секунд +
время 2 = 7.76417 секунд +
время 3 = 4.26e-06 секунд +
время 4 = 3.562e-06 секунд +

И чем длиннее строка - тем разительнее разница (хотя в принципе это объяснимо) ... или я что-то неправильно понимаю в интерпретации результатов?
Вложения
rvalue.cc
(2.14 КБ) 25 скачиваний

Аватара пользователя
Olej
Писатель
Сообщения: 14939
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: С++ в относительно новых стандартах

Непрочитанное сообщение Olej » 21 дек 2020, 02:36

Olej писал(а):
21 дек 2020, 02:32
И чем длиннее строка - тем разительнее разница
А что там происходит на коротких строках, с нормальной длиной, соизмеримой тому что мы имеем в реальной жизни?

Код: Выделить всё

olej@nvidia:~/2020_WORK/Zodiac_Systems/OWN-DRAFT-TESTs$ ./rvalue 50 40000000
время 1 = 1.88756 секунд +
время 2 = 1.7025 секунд +
время 3 = 0.668713 секунд +
время 4 = 0.557555 секунд +
Тоже неплохо, хотя и не так радикально.

Аватара пользователя
Olej
Писатель
Сообщения: 14939
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: С++ в относительно новых стандартах

Непрочитанное сообщение Olej » 28 дек 2020, 19:21

Olej писал(а):
21 дек 2020, 02:32
Меня интересует скоростные разницы при передаче и возврате строк (std::string) в/из функции.
Пока так:
В таких экспериментах нужно обязательно контроль того, что получается в итоге, особенно в итоге 1000000 копирований, когда промежуточный результат может исчезнуть в результате move-семантики...
Поэтому для достоверности переделаю вот так:

Код: Выделить всё

#include <iostream>     // std::cout
#include <chrono>       // high_resolution_clock

using namespace std;
using namespace std::chrono;

string fun1(string s)
{
  // do something with s .... 
  return s;
} 

string fun2(string& s)
{
  // do something with s .... 
  return s;
} 

string fun3(string&& s)
{
  // do something with s .... 
  return move( s );
} 

string fun4(string& s)
{
  // do something with s .... 
  return move( s );
} 

int main( int argc, char *argv[] ) {
  int len = 100000;                   // длина строки, argv[1]
  if( argc > 1 && atoi( argv[ 1 ] ) != 0 )
    len = atoi( argv[ 1 ] );
  int rep = 100000;                   // число повторений, argv[2]
  if( argc > 2 && atoi( argv[ 2 ] ) != 0 )
    rep = atoi( argv[ 2 ] );
  high_resolution_clock::time_point t1, t2;
  duration<double> time_span;
  const string inp = string( len, 'm' );
  string etallon[] = { inp, inp, inp, inp };
  for (int k = 0; k < sizeof( etallon ) / sizeof( etallon[0] ); k++ ) {
    t1 = high_resolution_clock::now();
    for (int i = 0; i < rep; i++)
      switch( k ) {
        case 0: 
        etallon[k] = fun1( etallon[k] );
        break;
      case 1:
        etallon[k] = fun2( etallon[k] );
        break;
      case 2:
        etallon[k] = fun3( move( etallon[k] ) );
        break;
      case 3:
        etallon[k] = fun4( etallon[k] );
        break;
      default:
        break;
    }
    t2 = high_resolution_clock::now();
    time_span = duration_cast<duration<double>>(t2 - t1);
    cout << "время " << k << " = " << time_span.count() << " секунд "
         << "\t" << ( etallon[k] == inp ? "+" : "-" );
    if( inp.length() <= 120 )
      cout << " : (" << etallon[k].length() << ") " << etallon[k];
    cout  << endl;
  }
}
На относительно коротких строках мы можем контролировать корректность передачи параметра и возврата значения, как-то так:

Код: Выделить всё

olej@nvidia:~/2020_WORK/Zodiac_Systems/OWN-DRAFT-TESTs$ ./rvalue 60 50000000
время 0 = 2.44577 секунд        + : (60) mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
время 1 = 2.25954 секунд        + : (60) mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
время 2 = 0.929781 секунд       + : (60) mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
время 3 = 0.749893 секунд       + : (60) mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
А на длинных стоках особенно наблюдать различия в скоростях (операций копирования и конструкторов):

Код: Выделить всё

olej@nvidia:~/2020_WORK/Zodiac_Systems/OWN-DRAFT-TESTs$ ./rvalue 10000000 1000
время 0 = 2.27179 секунд        +
время 1 = 2.42483 секунд        +
время 2 = 2.0953e-05 секунд     +
время 3 = 1.7181e-05 секунд     +
Вложения
rvalue.cc
(1.75 КБ) 24 скачивания

Аватара пользователя
Olej
Писатель
Сообщения: 14939
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: С++ в относительно новых стандартах

Непрочитанное сообщение Olej » 28 дек 2020, 19:31

Olej писал(а):
28 дек 2020, 19:21
Поэтому для достоверности переделаю вот так:
А ещё лучше (нагляднее) это можно сделать так:

Код: Выделить всё

#include <iostream>     // std::cout
#include <chrono>       // high_resolution_clock

using namespace std;
using namespace std::chrono;

int main( int argc, char *argv[] ) {
  int len = 100000;                   // длина строки, argv[1]
  if( argc > 1 && atoi( argv[ 1 ] ) != 0 )
    len = atoi( argv[ 1 ] );
  int rep = 100000;                   // число повторений, argv[2]
  if( argc > 2 && atoi( argv[ 2 ] ) != 0 )
    rep = atoi( argv[ 2 ] );
  high_resolution_clock::time_point t1, t2;
  duration<double> time_span;
  const string inp = string( len, 'm' );
  string etallon[] = { inp, inp, inp, inp };
  for (int k = 0; k < sizeof( etallon ) / sizeof( etallon[0] ); k++ ) {
    t1 = high_resolution_clock::now();
    for (int i = 0; i < rep; i++)
      switch( k ) {
        case 0: 
        etallon[k] = [](string s)->string{ return s; } ( etallon[k] );
        break;
      case 1:
        etallon[k] = [](string &s)->string{ return s; } ( etallon[k] );
        break;
      case 2:
        etallon[k] = [](string &&s)->string{ return s; } ( move( etallon[k] ) );
        break;
      case 3:
        etallon[k] = [](string &s)->string{ return move( s ); } ( etallon[k] );
        break;
      default:
        break;
    }
    t2 = high_resolution_clock::now();
    time_span = duration_cast<duration<double>>(t2 - t1);
    cout << "время " << k << " = " << time_span.count() << " секунд "
         << "\t" << ( etallon[k] == inp ? "+" : "-" );
    if( inp.length() <= 120 )
      cout << " : (" << etallon[k].length() << ") " << etallon[k];
    cout  << endl;
  }
}

Код: Выделить всё

olej@nvidia:~/2020_WORK/Zodiac_Systems/OWN-DRAFT-TESTs$ g++ rvalue2.cc -o rvalue2

Код: Выделить всё

olej@nvidia:~/2020_WORK/Zodiac_Systems/OWN-DRAFT-TESTs$ ./rvalue 60 10000000
время 0 = 0.536146 секунд 	+ : (60) mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
время 1 = 0.482363 секунд 	+ : (60) mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
время 2 = 0.195454 секунд 	+ : (60) mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
время 3 = 0.155921 секунд 	+ : (60) mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
Вложения
rvalue2.cc
(1.58 КБ) 24 скачивания

Ответить

Вернуться в «Программирование»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 2 гостя