LINUX.ORG.RU

В C++ добавят Rust

 , , ,


2

4

Привет, ЛОР! Я тебе покушать принёс.

Опубликован черновик расширения Safe C++, представляющего собой надмножество языка с возможностью отключать в коде Undefined Behaviour и прочие небезопасные штуки. Safe C++ добавляет в язык также borrow checker, pattern matching и другие функции, знакомые и любимые программистами на Rust. unsafe блоки входят в комплект.

Пример кода:

#feature on safety
#include <std2.h>

int main() safe {
  std2::vector<int> vec { 11, 15, 20 };

  for(int x : vec) {
    // Ill-formed. mutate of vec invalidates iterator in ranged-for.
    if(x % 2)
      mut vec.push_back(x);

    std2::println(x);
  }
}

Ошибка при сборке этого кода:

$ circle iterator.cxx -I ../libsafecxx/single-header/
safety: during safety checking of int main() safe
  borrow checking: iterator.cxx:10:11
        mut vec.push_back(x);
            ^
  mutable borrow of vec between its shared borrow and its use
  loan created at iterator.cxx:7:15
    for(int x : vec) {

Чтение за пределами обычных массивов также станет невозможным:

#feature on safety
#include <cstdint>

int main() safe {
  int array[4] { 1, 2, 3, 4 };
  size_t index = 10;

  // Panic on out-of-bounds array subscript.
  int x = array[index];
}

Результат:

$ circle subscript_array.cxx
$ ./subscript_array
subscript_array.cxx:9:17
int main() safe
subscript is out-of-range of type int[4]
Aborted (core dumped)

Таким образом, сишники и плюсисты наконец научатся писать нормальный код, который не падает с сегфолтами и не портит стэк. Так заживём!

Ссылка: https://safecpp.org/draft.html

★★★★★

Последнее исправление: hateyoufeel (всего исправлений: 2)

Ответ на: комментарий от alysnix

На самом деле идея крайне проста, вот C Safe Ptr v 0.0.0.0.1

#include <stdio.h>
#include <stdlib.h>

#define SAFE_PTR(TYPE) struct{TYPE p;}
#define SAFE_PTR_SET(NAME, V) NAME.p = V;
#define SAFE_PTR_CHECK(NAME) (NAME.p != NULL)
#define SAFE_PTR_GET(NAME) ({ \
  if (!NAME.p) abort(); \
  NAME.p; \
})


int main() {
  SAFE_PTR(const char*) ptr;
  SAFE_PTR_SET(ptr, "hello world");

  // ошибка компиляции
  // const char *s2 = ptr;

  if (SAFE_PTR_CHECK(ptr)) {
    puts(SAFE_PTR_GET(ptr));
  }
  SAFE_PTR_SET(ptr, NULL);
  const char *s = SAFE_PTR_GET(ptr); // abort
  printf("%s\n", s ? s : "null");

  return 0;
}

Сюда добавить можно NOT_NULL_SAFE_PTR, в сеттере задать ему через правила gcc не принимать выведенные компилятором NULL значения, и допускать только другие NOT_NULL_SAFE_PTR и сделать NOT_NULL_SAFE_PTR_FROM(OBJ) &OBJ

Вместо поля p сделать какой нибудь DONT_USE_INTERNAL_SAFE_PTR_FIELD и поставить хук на удаление из списка разработчиков при нахождении этой строки в коммите.

MOPKOBKA ★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 3)
Ответ на: комментарий от LamerOk

да легко, про что тоже намекали

import numpy as np
import imageio as iio
import time
from numba import jit

@jit
def img_fun(img):
    total = 0
    h, w, n = img.shape
    for i in range(h):
        for j in range(w):
            r = img[i, j, 0]
            g = img[i, j, 1]
            b = img[i, j, 2]
            total += r + g + b
    return total / ( h * w)

def main():
    img = iio.v3.imread('cheatsheet-rust.png')
    t1 = time.process_time()
    average = img_fun(img)
    t2 = time.process_time()
    print(f"CPU time: {t2 - t1:.2f} seconds")
    print(f"Average bit per pixel: {average:.2f}")
    print()
main()

замена арифметики на питоне вызовом сишного кода - это провал.

вообще это шизофазия какая-то, почему провал то? исходный код пишется на полноценном идиоматичном питоне (что и выражает самую суть понятия «пишем на таком-то языке» ), высокоуровнево и быстро, какая хрен разница что там вызывается? а если не сишка - то уже не провал?

zurg
()
Последнее исправление: zurg (всего исправлений: 2)
Ответ на: комментарий от MOPKOBKA

присваивание нула - легально. нелегально его разыменовывание.

как списки делать, если полю next, даже в односвязном нельзя присвоить null?

как лисп реализовать, если там cell - два указателя. и на них весь лисп стоит.

alysnix ★★★
()
Ответ на: комментарий от alysnix

А чистые указатели вообще использовать не надо. Если тебе нужно заполнить чистый указатель, и учесть NULL, то так надо писать:

const char *s;
if (SAFE_PTR_CHECK(ptr)) s = SAFE_PTR_GET(ptr);
else s = NULL;

MOPKOBKA ★★★★
()
Ответ на: комментарий от alysnix

получше всех остальных из распространенных. а то получается прям заговор против «годных» языков.

Чем лучше-то? C в том же clang транслируется сперва в llvm, а оттуда уже в бинарную форму. Поэтому разницы между Rust и C в бенчмарках почти не видно, например.

anonymous
()
Ответ на: комментарий от anonymous

Уровень llvm очень близок к С-форме, поэтому корректнее сказать что Rust транслируется в С, поэтому разницы и не видно. Более чисто в C транслируются например Nim, Chicken Scheme, так что С это новый ассемблер.

MOPKOBKA ★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 3)
Ответ на: комментарий от alysnix

а в чём это «получше» выражается? единственное, вроде, что делало сишку чуть ближе других системных языков - это ключевое слово register которое сейчас депрекейтед и один хрен давно ни на что не влияет

zurg
()
Ответ на: комментарий от MOPKOBKA

Уровень llvm очень близок к С-форме

Эм… нет?

поэтому корректнее сказать что Rust транслируется в С, поэтому разницы и не видно. Более чисто в C транслируются например Nim, Chicken Scheme, так что С это новый ассемблер.

Это было так какое-то время. Тот же Idris 2 с сишки уже свалил.

anonymous
()
Ответ на: комментарий от zurg

вообще это шизофазия какая-то, почему провал то?

Потому что ты как настоящий лоровец образца 2К24 конечно же просрал всю суть обсуждения:

Для вот этой вот срани C++ вообще не нужен,
Хуже того, это в лёгкую делается на пердоне с numpy.

В этой ветке не обсуждается, как программисты на питоне питонячат свои питонопортянки. В этой ветке темы обсуждается, как легко и непринуждённо питон заменяет кресты при вычислениях с матрицами.

Именно этот тезис надо доказать.

Аргументы уровня «двоичный код вызванный из питона работает так же быстро, как вызванный из си» лишь демонстрируют неспособность понять предмет беседы.

LamerOk ★★★★★
()
Последнее исправление: LamerOk (всего исправлений: 1)
Ответ на: комментарий от zurg

в сишечке нет ни одной фичи, что выражалась бы сложным кодом при компиляции. там самый сложный код у оператора switch.

там нет ни конструкторов, ни исключений с раскруткой стека, ни сборки мусора, ни виртуальных методов, ни динамического преобразования типа, ни встроенных коллекций, каких-то «ящиков» как в русте, короче вообще ничего такого. и все это тривиально транслируется в машинный код.

alysnix ★★★
()
Ответ на: комментарий от alysnix

в сишечке нет ни одной фичи, что выражалась бы сложным кодом при компиляции

В сишечке весь код это одна большая сложная фича. Сравни вывод с -O0 и вывод с -O2.

там нет ни конструкторов

Есть конечно. Просто их все руками пишут. Они от этого никуда не деваются.

ни виртуальных методов

Лолшто? foo->doit(foo) это, наверное, первый сишный паттерн, который учат детишки, как только пишут что-то сложнее hello world.

ни встроенных коллекций

Поэтому все пишут свои linked list, которые работают, как и положено наивным реализациями, небыстро.

каких-то «ящиков» как в русте

Ящики как раз есть. Это указатели.

все это тривиально транслируется в машинный код

Транслировалось в 80-х в PDP-11. Сейчас это транслируется через два миллиона строк эвристик, вычислений и прочего страдания. Если этого не происходит, сишка начинает сосать как не в себя.

anonymous
()
Ответ на: комментарий от anonymous

Лолшто? foo->doit(foo) это, наверное, первый сишный паттерн, который учат детишки, как только пишут что-то сложнее hello world.

Так пишут только детишки в Hello World

Транслировалось в 80-х в PDP-11. Сейчас ...

Процессоры делаются с оглядкой на компилятор, поэтому транслируется так же хорошо. В x86 даже функции из string.h аппаратные, уже даже не тот код что был при while(*++)

MOPKOBKA ★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 1)
Ответ на: комментарий от MOPKOBKA

Так пишут только детишки в Hello World

Так пишет весь Linux Kernel. Крупнейший опенсурсный проект на C, если что.

Процессоры делаются с оглядкой на компилятор, поэтому транслируется так же хорошо. В x86 даже функции из string.h аппаратные.

:DDDDDDDDDDDDDDD

anonymous
()
Ответ на: комментарий от anonymous

не надо выдавать нечто написанное на языке, как фичу самого языка. самописные конструкторы, инициализации, списки и проч никак не являются встроенными возможностями си. встроенные возможности си направлены то, чтобы все перечисленное и много чего еще, написать максимально прозрачно и без подводных камней.

alysnix ★★★
()
Ответ на: комментарий от MOPKOBKA

Ок, ответить тебе нечем, а про ядро ты соврал.

Открой сетевой стек и посмотри как с sk работают. Открой блочный стек и посмотри как с bio и request работают. Открой крипту… ну ты понял.

anonymous
()
Ответ на: комментарий от anonymous

Открыл, а там vtable вместо foo->bar(foo)

https://elixir.bootlin.com/linux/v5.5.1/source/include/linux/phy/phy.h#L134

а foo->bar(foo) применяется где

1. мало методов, нету смысла выносить в отдельную таблицу

2. нужно переопределять методы в процессе

и вот имеет уже даже более лучшую чем в С++ модель! Которую еще и контролировать можно.

MOPKOBKA ★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 1)
Ответ на: комментарий от alysnix

не надо выдавать нечто написанное на языке, как фичу самого языка

Никто и не выдает это за фичу. Просто ты говоришь что этого нет. Это есть, просто кривое, корявое, и постоянно переписываемое. Потому что без этих вещей не получается писать сложные программы.

самописные конструкторы, инициализации, списки и проч никак не являются встроенными возможностями си

Они являются необходимостью для всего того софта, что сейчас пишется на C. То, что их нет в языке, это баг, а не фича. То, какую дичь творят, чтобы классы реализовать (привет, container_of), это тоже баг, а не фича.

встроенные возможности си направлены то, чтобы все перечисленное и много чего еще, написать максимально прозрачно и без подводных камней

Ты просто хочешь в это верить. По факту там просто свалка из костылей, которые кое-как сбили в рабочую форму на трех-четырех компиляторах.

anonymous
()
Ответ на: комментарий от MOPKOBKA

Тут нет никакой ошибки, лол. Это одно и то же – виртуальные функции. Просто в одном случае они в теле самого объекта (e.g. bio), а в другом – в таблице (e.g. file), ссылка на которую кладется в тело объекта. Тебе буковка «v» в «vtable» ни на что не намекает? :DDDD

anonymous
()
Ответ на: комментарий от anonymous

Я знаю что такое виртуальные функции и что такое vtable, в оригинальном комментарии я указывал на отсутствие vtable, потом мы перешли к тому что она все же есть и действительно есть случаи когда ее лучше не использовать.

MOPKOBKA: и вот имеет уже даже более лучшую чем в С++ модель! Которую еще и контролировать можно.

В чем недостаток С?

MOPKOBKA ★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 2)
Ответ на: комментарий от alysnix

получше всех остальных из распространенных. а то получается прям заговор против «годных» языков.

Что это значит? Если мы говорим про memory layout различных типов, то у каждого языка есть описание того, как именно его структуры размещаются в памяти. Даже у Rust это довольно просто.

Если мы говорим про преобразование исходного кода в бинарный, то сишные компиляторы творят просто лютейшую тёмную магию и ты никогда не угадаешь, во что именно превратится код сложнее «Hello World».

в сишечке нет ни одной фичи, что выражалась бы сложным кодом при компиляции.

Это потому что в Си просто нет фич. В итоге, всё это работает ровно наоборот: гигантские лапши сишного кода компилятор часто превращает в довольно короткий ассемблерный листинг. Например, выкидывая целые функции и заменяя их на пару вызовов какой-нибудь SIMD.

hateyoufeel ★★★★★
() автор топика
Последнее исправление: hateyoufeel (всего исправлений: 1)
Ответ на: комментарий от hateyoufeel

В итоге, всё это работает ровно наоборот: гигантские лапши сишного кода компилятор часто превращает в довольно короткий ассемблерный листинг.

вы такие страсти рассказываете, что я решил в темное время к компилятору не подходить. мало ли чего.

alysnix ★★★
()
Ответ на: комментарий от alysnix

Просто, как и прочие сишники, ты находишься в плену иллюзий про низкоуровневость C, близость к железу и прочие милые глупости. C настолько же далек от того, что реально делает процессор, как и все остальные.

anonymous
()
Ответ на: комментарий от MOPKOBKA

и вот имеет уже даже более лучшую чем в С++ модель! Которую еще и контролировать можно.

В rust кстати эта же модель используется, vtable не привязан жестко к объекту, в самом объекте нет указателя на него. Гибкость получается намного больше чем в C++, можно привязывать vtable (в случае раста реализовать трейты) даже к любым примитивным объектам (например к i32 и т. п.), сами объекты занимают меньше места в памяти. Еще из плюсов обобщенные функции все разрешают в compile time и vtable вообще выкидывается. Минус заметный только один указатели на динамические объекты получаются толстыми и состоят из указателя на объект + указатель на vtable.

anonymous
()
Ответ на: комментарий от anonymous

vtable - это таблица виртуальных методов, а не обычных. у обьекта должна быть ссылка на нее по любому, если такие методы есть.

дальше уже вопрос - являются ли виртуальные методы собственными методами обьекта или его класса. если класса - то таблица одна на класс. если обьекта - то у каждого обьекта таблица может быть своя, но такой подход избыточен, и непонятно зачем оно вообще надо для программирования.

alysnix ★★★
()
Ответ на: комментарий от anonymous

В rust кстати эта же модель используется

Конечно же нет. В C у тебя прямо в структуре есть специальное поле этой структуры, где лежит указатель на определенный тип. И это нерасширяемая история. В Rust ты можешь прикрутить сверху трейты и расширять произвольные типы, включая примитивные. В C ты этого не можешь. Но в C это и не проблема, потому что в C нет нормальных библиотек.

anonymous
()
Ответ на: комментарий от alysnix

если обьекта - то у каждого обьекта таблица может быть своя, но такой подход избыточен, и непонятно зачем оно вообще надо

Это типовой сценарий с заменой плагинов во время работы. При смене плагина экземпляры нового будут работать с новыми методами, а экземпляры старого - продолжат работать по старому.

LamerOk ★★★★★
()
Ответ на: комментарий от alysnix

vtable - это таблица виртуальных методов, а не обычных. у обьекта должна быть ссылка на нее по любому, если такие методы есть.

Про это и писал выше, в C++ в каждом объекте будет скрытый указатель на vtable, в rust такого указателя нет, но взамен указатель на сам объект будет толстым состоящим из двух указателей один на данные, второй на vtable. Это и позволяет увеличить гибкость.

anonymous
()
Ответ на: комментарий от hateyoufeel

optional<void*>

Я, кстати, удивлен, что ни у кого из убийц си не видел еще такой фичи: запрет (или задвигание подальше) нулевых указателей, введение optional с нулевым оверхедом (хотя бы даже на уровне языка). Такой си я бы купил.

В этом направлении только ДеВолт двинулся - ввел nullable в систему типов. Но я не уверен, что его проверка компилятором на if для них точна.

Ну и optional приличному языку все равно нужен и устраняет кучу багов с магическими константами (из которых NULL - просто частный случай).

unsigned ★★★★
()
Ответ на: комментарий от unsigned

vtable - это таблица виртуальных методов, а не обычных. у обьекта должна быть ссылка на нее по любому, если такие методы есть.

В safe rust же все так и есть, правда в unsafe все-равно доступны полноценные указатели.

anonymous
()
Ответ на: комментарий от unsigned

Я, кстати, удивлен, что ни у кого из убийц си не видел еще такой фичи: запрет (или задвигание подальше) нулевых указателей, введение optional с нулевым оверхедом (хотя бы даже на уровне языка). Такой си я бы купил.

В safe rust же все так и есть, правда в unsafe все-равно доступны полноценные указатели.

Выше промахнулся с цитированием :)

anonymous
()
Ответ на: комментарий от unsigned

Я, кстати, удивлен, что ни у кого из убийц си не видел еще такой фичи: запрет (или задвигание подальше) нулевых указателей, введение optional с нулевым оверхедом (хотя бы даже на уровне языка). Такой си я бы купил.

Ты Rust описываешь сейчас.

hateyoufeel ★★★★★
() автор топика
Ответ на: комментарий от unsigned

В этом направлении только ДеВолт двинулся - ввел nullable в систему типов.

А C3 и Zig?

Но я не уверен, что его проверка компилятором на if для них точна.

Я тоже, для C3 и Zig. :)

dataman ★★★★★
()
Ответ на: комментарий от alysnix

то есть, если у тебя пять указателей на один обьект, у тебя еще и пять указателей на «vtable». гибко, а чо!

Конечно, ведь эти пять указателей могут быть указателями на разные vtable, это и есть гибкость. А с экономией памяти все неоднозначно получается, в случае C++ у нас у объекта (с вирт. функциями) всегда оверхед на размер указателя (на vtable), зато сами указатели тонкие (8 байт для 64 битной системы), в случае же раста оверхеда постоянного у объекта нет, зато указатели толстые (16 байт для 64 битной системы). И то и то может быть как плюсом так и минусом.

anonymous
()
Ответ на: комментарий от anonymous

в случае же раста оверхеда постоянного у объекта нет

да уж нет. на объект(хоть в расте, хоть где) должен быть хотя один указатель, иначе он недостижим. то есть, как минимум один адрес vtable будет аллокирован.

просто в плюсах, он внутри обьекта, а в расте хотя бы в единственном указателе. значит расте будет или как в плюсах, или хуже.

alysnix ★★★
()
Ответ на: комментарий от anonymous

Я пишу числодробилки и там эти такты очень заметны.

Так то есть опция (макрос) сборки которая автоматом чекает границы std::vector, есть санитайзеры и есть valgrind наконец. Не говоря уже про ассерты. Напуркуа пухлый стандарт ещё больше ради этого раздувать непонятно

AntonI ★★★★★
()