C ++ "moderno": uma sessão de lamentações com lamentações

Haverá uma longa parede de texto, com um tipo de pensamentos aleatórios. Ideias-chave:


  1. No C ++, o tempo de compilação é muito importante,
  2. Construir desempenho sem otimizações também é importante,
  3. A carga cognitiva é ainda mais importante. Não discutirei esse ponto particularmente, mas se a linguagem de programação me faz sentir burra, é improvável que eu a use, muito menos amá-la. C ++ faz isso comigo constantemente .

A publicação do blog Standard Ranges de Eric Niebler sobre intervalos C ++ 20 circulou recentemente todo o universo do twitter, acompanhada por vários comentários não tão lisonjeiros (para dizer o mínimo!) Sobre o estado do C ++ moderno.



Até eu contribuí ( link ):


Este exemplo de triplos pitagóricos em C ++ 20, na minha opinião, parece monstruoso. E sim, entendo que os intervalos podem ser úteis, as projeções podem ser úteis e assim por diante. No entanto, o exemplo é assustador. Por que alguém precisaria disso?

Vamos dar uma olhada mais de perto em tudo isso.



Tudo isso ficou um pouco fora de controle (mesmo uma semana depois, os comentários continuaram subindo nessa árvore de tópicos!).


Agora, tenho que me desculpar com Eric por começar com seu artigo; meu grito de Yaroslavna será principalmente sobre a "condição geral do C ++". Um punhado de caras gamedev amargurados há um ano se deparou com uma explicação do Boost.Geometry da mesma maneira, e o mesmo aconteceu com dezenas de outros aspectos do ecossistema C ++.


Mas você sabe, o Twitter não é o melhor lugar para conversas confidenciais etc., etc. Teremos que expandir o pensamento aqui e agora!


Triplos pitagóricos no estilo das fileiras C ++ 20


Mantenha o texto de exemplo completo da postagem de Eric:


//     C++20.
//    N  .
#include <iostream>
#include <optional>
#include <ranges>   //   !

using namespace std;

// maybe_view    0  1 
template<Semiregular T>
struct maybe_view : view_interface<maybe_view<T>> {
  maybe_view() = default;
  maybe_view(T t) : data_(std::move(t)) {
  }
  T const *begin() const noexcept {
    return data_ ? &*data_ : nullptr;
  }
  T const *end() const noexcept {
    return data_ ? &*data_ + 1 : nullptr;
  }
private:
  optional<T> data_{};
};

// "for_each"   , 
//       ,
//        .
// (   constrained lambdas  C++20.)
inline constexpr auto for_each =
  []<Range R,
     Iterator I = iterator_t<R>,
     IndirectUnaryInvocable<I> Fun>(R&& r, Fun fun)
        requires Range<indirect_result_t<Fun, I>> {
      return std::forward<R>(r)
        | view::transform(std::move(fun))
        | view::join;
  };

// "yield_if"  bool  , 
//    0  1 .
inline constexpr auto yield_if =
  []<Semiregular T>(bool b, T x) {
    return b ? maybe_view{std::move(x)}
             : maybe_view<T>{};
  };

int main() {
  //     :
  using view::iota;
  auto triples =
    for_each(iota(1), [](int z) {
      return for_each(iota(1, z+1), [=](int x) {
        return for_each(iota(x, z+1), [=](int y) {
          return yield_if(x*x + y*y == z*z,
            make_tuple(x, y, z));
        });
      });
    });

    //   10 
    for(auto triple : triples | view::take(10)) {
      cout << '('
           << get<0>(triple) << ','
           << get<1>(triple) << ','
           << get<2>(triple) << ')' << '\n';
  }
}

, , «Getting Lazy with C++», N :


void printNTriples(int n)
{
    int i = 0;
    for (int z = 1; ; ++z)
        for (int x = 1; x <= z; ++x)
            for (int y = x; y <= z; ++y)
                if (x*x + y*y == z*z) {
                    printf("%d, %d, %d\n", x, y, z);
                    if (++i == n)
                        return;
                }
}

:


, . , , ? , ?

(list comprehensions) . , - , C++ , - Haskell . C++20 , . .


C++


, , C/C++ («» — , «, », ). , :


// simplest.cpp
#include <time.h>
#include <stdio.h>

int main()
{
    clock_t t0 = clock();

    int i = 0;
    for (int z = 1; ; ++z)
        for (int x = 1; x <= z; ++x)
            for (int y = x; y <= z; ++y)
                if (x*x + y*y == z*z) {
                    printf("(%i,%i,%i)\n", x, y, z);
                    if (++i == 100)
                        goto done;
                }
    done:

    clock_t t1 = clock();
    printf("%ims\n", (int)(t1-t0)*1000/CLOCKS_PER_SEC);
    return 0;
}

: clang simplest.cpp -o outsimplest. 0.064 , 8480 , 2 ( : 2018 MacBookPro; Core i9 2.9GHz; — Xcode 10 clang).


(3,4,5)
(6,8,10)
(5,12,13)
(9,12,15)
(8,15,17)
(12,16,20)
(7,24,25)
(15,20,25)
(10,24,26)
...
(65,156,169)
(119,120,169)
(26,168,170)

! , («Debug») ; («Release»): clang simplest.cpp -o outsimplest -O2. 0.071 (8480 ), 0 ( , clock()).


, , . « » ( , «» « » ). , , -, N , .


— , , . :


// simple-reusable.cpp
#include <time.h>
#include <stdio.h>

struct pytriples
{
    pytriples() : x(1), y(1), z(1) {}
    void next()
    {
        do
        {
            if (y <= z)
                ++y;
            else
            {
                if (x <= z)
                    ++x;
                else
                {
                    x = 1;
                    ++z;
                }
                y = x;
            }
        } while (x*x + y*y != z*z);
    }
    int x, y, z;
};

int main()
{
    clock_t t0 = clock();

    pytriples py;
    for (int c = 0; c < 100; ++c)
    {
        py.next();
        printf("(%i,%i,%i)\n", py.x, py.y, py.z);
    }

    clock_t t1 = clock();
    printf("%ims\n", (int)(t1-t0)*1000/CLOCKS_PER_SEC);
    return 0;
}

. 168 , .


pytriples, next() ; , . , .


, , for- , , , . , , ( ), .


C++ - , , , , «» ( «Ranges, Code Quality, and the Future of C++»); ( , C++ ):


generator<std::tuple<int,int,int>> pytriples()
{
    for (int z = 1; ; ++z)
        for (int x = 1; x <= z; ++x)
            for (int y = x; y <= z; ++y)
                if (x*x + y*y == z*z)
                    co_yield std::make_tuple(x, y, z);
}

C++


C++20 ? , :


auto triples =
    for_each(iota(1), [](int z) {
        return for_each(iota(1, z+1), [=](int x) {
            return for_each(iota(x, z+1), [=](int y) {
                return yield_if(x*x + y*y == z*z,
                    make_tuple(x, y, z));
                });
            });
        });

. , , , . , C++ , , C++ (« ? , !») — . return- , , , .


, , .


, , , C++, , :


template<Semiregular T>
struct maybe_view : view_interface<maybe_view<T>> {
  maybe_view() = default;
  maybe_view(T t) : data_(std::move(t)) {
  }
  T const *begin() const noexcept {
    return data_ ? &*data_ : nullptr;
  }
  T const *end() const noexcept {
    return data_ ? &*data_ + 1 : nullptr;
  }
private:
  optional<T> data_{};
};
inline constexpr auto for_each =
  []<Range R,
     Iterator I = iterator_t<R>,
     IndirectUnaryInvocable<I> Fun>(R&& r, Fun fun)
        requires Range<indirect_result_t<Fun, I>> {
      return std::forward<R>(r)
        | view::transform(std::move(fun))
        | view::join;
  };
inline constexpr auto yield_if =
  []<Semiregular T>(bool b, T x) {
    return b ? maybe_view{std::move(x)}
             : maybe_view<T>{};
  };

, - , - , Perl , Brainfuck — , . C++ 20 . , , , .


, , maybe_view, for_each, yield_if — « », ; , … .


« »


:



, , C++, , .


C++20 , , range-v3 ( ), .


// ranges.cpp
#include <time.h>
#include <stdio.h>
#include <range/v3/all.hpp>

using namespace ranges;

int main()
{
    clock_t t0 = clock();

    auto triples = view::for_each(view::ints(1), [](int z) {
        return view::for_each(view::ints(1, z + 1), [=](int x) {
            return view::for_each(view::ints(x, z + 1), [=](int y) {
                return yield_if(x * x + y * y == z * z,
                    std::make_tuple(x, y, z));
            });
        });
    });

    RANGES_FOR(auto triple, triples | view::take(100))
    {
        printf("(%i,%i,%i)\n", std::get<0>(triple), std::get<1>(triple), std::get<2>(triple));
    }

    clock_t t1 = clock();
    printf("%ims\n", (int)(t1-t0)*1000/CLOCKS_PER_SEC);
    return 0;
}

0.4.0 (9232b449e44 22 2018 ), clang ranges.cpp -I. -std=c++17 -lc++ -o outranges. 2.92 , 219 , 300 .


, . (clang ranges.cpp -I. -std=c++17 -lc++ -o outranges -O2) 3.02 , 13976 , 1 . , , .


.


— C++


2.85 , « C++».


, « 3 » — , . CPU . , clang (SQLite) , 220 ? 0.9 . , 5 ?


++ , . ? , - (Chromium, Clang/LLVM, UE4, ). , C++, , , , . , , C++ , , , , .


« » #include, . , C++ /.


range-v3 1.8 , ! , 30 , 102 . « C++» 720 .


/ ! — , . . precompiled header (pch.h : #include <range/v3/all.hpp>, pch.h, PCH: clang -x c++-header pch.h -I. -std=c++17 -o pch.h.pch, pch: clang ranges.cpp -I. -std=c++17 -lc++ -o outranges -include-pch pch.h.pch). 2.24 . , PCH 0.7 . 2.1 , , C++ :(



150 . , 2 3 . , 10 . , ? ?


, , , . ; , ( ). , , , , 10 100 , «». , . , , «» «». , 2 .


, (-O2 clang) « C++»… , , «zero cost abstractions», - . , .


! , , . , . - , . , , .


Arseny Kapoulkine «Optimizing OBJ loader» YouTube, , 10 , STL (). () , STL Microsoft .


, «STL — »; STL, (EASTL libc++ ), - Microsoft STL , « ».


, , ! — «STL », , - . (, STL, , C++, ).



« » C#:


using System;
using System.Diagnostics;
using System.Linq;

class Program
{
    public static void Main()
    {
        var timer = Stopwatch.StartNew();
        var triples =
            from z in Enumerable.Range(1, int.MaxValue)
            from x in Enumerable.Range(1, z)
            from y in Enumerable.Range(x, z)
            where x*x+y*y==z*z
            select (x:x, y:y, z:z);
        foreach (var t in triples.Take(100))
        {
            Console.WriteLine($"({t.x},{t.y},{t.z})");
        }
        timer.Stop();
        Console.WriteLine($"{timer.ElapsedMilliseconds}ms");
    }
}

, . C#:


var triples =
    from z in Enumerable.Range(1, int.MaxValue)
    from x in Enumerable.Range(1, z)
    from y in Enumerable.Range(x, z)
    where x*x+y*y==z*z
    select (x:x, y:y, z:z);

C++:


auto triples = view::for_each(view::ints(1), [](int z) {
    return view::for_each(view::ints(1, z + 1), [=](int x) {
        return view::for_each(view::ints(x, z + 1), [=](int y) {
            return yield_if(x * x + y * y == z * z,
                std::make_tuple(x, y, z));
        });
    });
});

, . ? , C# LINQ :


var triples = Enumerable.Range(1, int.MaxValue)
    .SelectMany(z => Enumerable.Range(1, z), (z, x) => new {z, x})
    .SelectMany(t => Enumerable.Range(t.x, t.z), (t, y) => new {t, y})
    .Where(t => t.t.x * t.t.x + t.y * t.y == t.t.z * t.t.z)
    .Select(t => (x: t.t.x, y: t.y, z: t.t.z));

C#? Mac, Mono ( C#) 5.16 mcs Linq.cs 0.20 . « C#» 0.17 .


, LINQ 0.03 . 3 C++ — 100 !


, ?


, - .


, Unity , « Boost ». , , , - Boost.Asio, , , asio.h <windows.h>, .


STL. , , EASTL — , /, , . - , (unordered_map STL , separate chaining; - ). .


.


, ( , ) , «» C++ , ( ! ). , « » , ( , ! ).


, ( ) , , « » . () , , ‎(ノಥ益ಥ)ノ ┻━┻, «, , ». : «, - ?». : «, !». (, , - ). , C C++, , . ; , « — », — ; .


, C++, . , « — », « , , ».


C++?


. , « , , ». , C++ , , !


- , , C++ «» .


C/C++. , - 16 . Boost, : «, , !». , .


, , Formula 1 . ? . ? . , ? ! 99% , ? .


:


, . « ». , C++ 1) 2) , , , , , . 2.

, , C++ , , . . ( ) . ( EASTL). , C++ , (Jai) (Rust, C#).



, , « » , — . , , , , , , . ; , - , — . !


, - , C++, STL . - , .


, . — .


, , , . , , , , , .


, - , ? «» « », . , ? // — . , «, », .


  • « , , » — !
  • « , , , » — !
  • «, » — !
  • « , , , » — , , .

« , , C++», . , « Boost!» C++, , Boost — - .


, , . C C++ , ( Rust, ). C++, , , , , .


, , — . C++, « » ( - ); - . , ; , . , , - , «C++ — , », , .


, (C++11/14/17) C++ — , , constexpr if , . , , STL ( , ), , , ///… , , , .


. 19-20 C++ Russia 2019. , . — Arno Schödl, CTO Think-Cell, «Text Formatting For a Future Range-Based Standard Library». , ? . , ( ).

Source: https://habr.com/ru/post/pt438260/


All Articles