28 Ağustos 2020 Cuma

ranges Kütüphanesi

C++20 ile geliyor. Temel amacı lazy evaluation

Neden Aynı Algoritma İsimleri
STL içinde iterator alan algoritma isimleri aynı zamanda ranges içinde de var. Sebebi STL algoritmalarını iterator kullanması, ancak ranges iterator dönmüyor, sentinel denen bir nesne dönüyor. Dolayısıyla şu kod derlenmez
#include <ranges>
#include <numeric>
#include <iostream>

int main() {
  auto rng = std::ranges::istream_view<int>(std::cin);
  std::cout << std::accumulate(std::ranges::begin(rng), std::ranges::end(rng), 0);
Açıklaması şöyle
The problem is that the end of a C++ range is not, in the general case, an iterator, but rather, a sentinel. A sentinel can have a different type than an iterator and admit fewer operations - as, generally speaking, you mostly need to compare against it to know you've reached the end of the range, and may not be allowed to just work with it like any iterator.
Açıklaması şöyle
While a lot of algorithms added corresponding overloads into std::ranges, the ones in <numeric> were left out. There is a std::accumulate but there is no std::ranges::accumulate
Dolayısıyla C++ içinde birbirine uyumsuz stream, iterator nesneleri yanında bir range nesnesi geldi.

ranges Generators yazısına taşıdım.

std::ranges::contiguous_range metodu
Parametrenin std::ranges::random_access_range olması gerekir. Tanımı şöyle
template<class T>
concept contiguous_range =
  ranges::random_access_range<T> &&
  std::contiguous_iterator<ranges::iterator_t<T>> &&
  requires(T& t) {
    { ranges::data(t) } ->
Şöyle yaparız
struct ntbs_sentinel {
  bool operator==(char const* p) const {
    return *p == '\0';

struct ntbs {
  char const* p;
  char const* begin() const { return p; }
  ntbs_sentinel end() const { return {}; }

std::ranges::fill metodu
Şöyle yaparız.
#include <algorithm>
#include <cassert>
#include <span>

namespace ranges = std::ranges;

int main()
  int arr[] {1, 7, 3, 2, 0, 5, 0, 8};

  const std::span spn{arr};
  ranges::fill(spn, 173);               // this compiles

  assert(ranges::count(arr, 173) == 8); // passes
std::ranges::find metodu
İki tane overload edilmiş hali var. Bunlar şöyle
namespace ranges {
  template<input_­iterator I, sentinel_­for<I> S, class T, class Proj = identity>
    requires indirect_­binary_­predicate<ranges::equal_to, projected<I, Proj>, const T*>
    constexpr I find(I first, S last, const T& value, Proj proj = {});

  template<input_­range R, class T, class Proj = identity>
    requires indirect_­binary_­predicate<ranges::equal_to,
                                       projected<iterator_t<R>, Proj>, const T*>
    constexpr borrowed_iterator_t<R>
      find(R&& r, const T& value, Proj proj = {});
std::ranges::istream_view metodu
Açıklaması şöyle
There is a utility to wrap a C++20 range into a C++17-compatible one: views::common
istream_view's iterators aren't copyable, and in C++17 all iterators must be. So there isn't really a way to provide C++17-compatible iterators based on istream_view.
Şöyle yaparız
auto rng = std::ranges::istream_view<int>(ints) | std::views::common;
std::cout << std::accumulate(rng.begin(), rng.end(), 0);
std::ranges::range metodu
Şöyle yaparız.
template<typename T>
concept nested_range = std::ranges::range<T> && 
Açıklaması şöyle
If you want to ask if a type is a range which itself contains a range, that's simply applying the std::range type twice
std::ranges::swap metodu
Şöyle yaparız
#include <concepts>
#include <iostream>

struct dummy {
  friend void swap(dummy& a, dummy& b) {
    std::cout << "ADL" << std::endl;

int main()
  int a{};
  int b{};
  dummy c{};
  dummy d{};
  std::ranges::swap(a, b);
  std::ranges::swap(c, d); // Ok. Prints "ADL" on console.

25 Ağustos 2020 Salı


Açıklaması şöyle
I don't know what assembler you've been using, but I've never used one that allowed one to declare variables with types. All the assemblers that I've ever used allowed one to reserve space and, to use any available op-code to access that space and operate on its content.
MOV Instruction
Aslında Copy anlamına gelir. Açıklaması şöyle.
Many processors have an instruction called "move" (sometimes spelled MOV) which copies data from one location (the "source") to another (the "destination") in registers and/or memory. It does not do anything to the "source".

This is analogous to the "copy" (or "cp") command in a filesystem. In a "move" (or "mv") in the filesystem, the "source" would no longer be there. It is also contrary to the simple English meaning of "move"... after you move an object, it exists in its new location but no longer in its previous location.
MULTIPLY Instruction
İlk işlemcilerde çarpma instruction yoktu. Açıklaması şöyle
Fast multiplier circuits as used today take enormous amounts of logic, far beyond what would have been cost-effective (or perhaps even possible) in the mid-70s for an inexpensive microprocessor. Even slow multiplier circuits (as would appear later on chips like the 6809, 68000 or 8086) use a fair bit of logic and would have very considerably added to the cost, perhaps forcing a multi-chip design with all the complications that entails.

The first lines of microprocessors were primarily targeted at embedded control applications where rapid multiplication is rarely needed, so that was likely a factor too.
Şöyle yaparız.
inline int my_int (double x)
  int r;
  asm ("fldl %1\n"
       "fistpl %0\n"
  return r;
asm volatile
Şöyle yaparız. escape() ve clobber() metodları arasında derleyicinin optimizasyon yapmasını engeller.
static void escape (void *p) {
  asm volatile ("" : : "g"(p) : "memory");

static void clobber () {
  asm volatile ("" : : : "memory");

void benchmark ()
  vector<int> v;
  v.reserve (1);
  escape (v.data());
  v.push_back (10);
  clobber ()
Microsoft Extension
__asm blok kullanılır. -masm=intel seçeneği ile derlenir. Şöyle yaparız.
#include "stdio.h"

int f()
    mov eax, 42 // setting EAX to 42 here

int main()
  int i = f();
  printf("%i\n", i);

  return 0;