Как стать автором
Обновить
105
0
Евгений @Izaron

Программист C++

Отправить сообщение

Опять же возникает вопрос, что более вероятно - что все картины отдадут в макулатуру и на месте Эрмитажа сделают картофельное поле, или что сайт отключат за "неуплату" и держатели бирок пойдут лесом?

Да, картины-подделки бывают, но обратите внимание - вы говорите про единичные случаи, тогда как в случае с NFT под сомнение ставится само понятие как таковое.

А чем современная банковская система не устраивает? Тем, что в ней намайнить бабла немножко сложнее, чем накупить асики и подключить к программке?

Таким образом - делаем в cron ежеминутную задачу - отслеживать конкретный лог-файл на предмет ключевого слова/фразы/цифры и т.д.

Мне кажется, это одно из худших проявлений Hyrum's law. Мне было бы неприятно, если после релиза с изменением логов стали стучаться в личку и требовать вернуть их назад, а то не считается статистика за день. Настолько "разумную жизнь" на основе логов надо убирать как можно быстрее.

Про "стиль C++" в списке, а именно про это

pub struct Item<T> {
    data: T,
    next: *const Item<T>,
}

pub struct List<T> {
    head: *const Item<T>,
}

Это скорее реализация std::forward_list (односвязный список), чем std::list (двусвязный).

В C++ можно передать кастомный аллокатор, если нет желания использовать дефолтный.

Эта строка:

data: T,

Что насчет placement new? В вашем примере объект создается и только потом мувается в это место, но он мог бы сразу создаваться. Это "стиль C++" с С++11. Вместо этой строки должен быть растовый аналог std::aligned_storage<T>, а в методе push - perfect forwarding аргументов.

Я имел в виду названия полей, то есть из записи

repeated string Pepper = 13;

В сериализованном виде слово "Pepper" пропадет, но к сожалению я не так прочитал абзац (неправильно понял), посчитал что "Pepper" и есть "ключ" (по аналогии с JSON).

последовательность бит из ключей и значений

На самом деле в сериализованном представлении ключи выкинуты (остались значения), так как считается, что контрагент знает про тот же .proto файл и может с его помощью расшифровать бинарный поток. Без .proto файла бинарный поток понять человеку/программе невозможно, читабельны только string-поля.

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

Для удобства можно считать, что нас вершин 1024 штуки (степень двойки специально), нумерация от 0 до 1023 включительно.

Можно завести `std::array<int, 1024> keys` (ключи отсортированы) и `std::array <int, 1024> values`.

Поиск по ключу займет не более чем 10 сравнений, по одному биту каждый раз. Индексы ключей у нас от `0000000000` до `1111111111`, поэтому надо смотреть ключ посередине - по индексу `1000000000`. Если он больше, то единицу отнимаем. И потом смотрим следующий бит.

    int some_key = XXX;
    int index = 0;
    for (int i = BYTES - 1; i >= 0; --i) {
        if (some_key >= keys[index + (1 << i)]) {
            index += (1 << i);
        }
    }
    return value[index];

Да уж, это насколько же программисту надо плохо думать о разработчиках компиляторов, чтобы полагать, что x /= 2 не заменится на x >>= 1 сам)

Да, язык хорошо живет без рефлексии много лет, но новый функционал это новые возможности.

Можно подсмотреть, что крутого есть в других языках. Фреймворк Spring в Java в этом плане очень мощно развит.

Так выглядит обработчик http-запросов:

в Java
@RestController
class EmployeeController {

  private final EmployeeRepository repository;

  EmployeeController(EmployeeRepository repository) {
    this.repository = repository;
  }

  @GetMapping("/employees")
  List<Employee> all() {
    return repository.findAll();
  }

  @PostMapping("/employees")
  Employee newEmployee(@RequestBody Employee newEmployee) {
    return repository.save(newEmployee);
  }

  @GetMapping("/employees/{id}")
  Employee one(@PathVariable Long id) {
    return repository.findById(id)
      .orElseThrow(() -> new EmployeeNotFoundException(id));
  }

  @DeleteMapping("/employees/{id}")
  void deleteEmployee(@PathVariable Long id) {
    repository.deleteById(id);
  }
}

Примерно так можно бы сделать в C++, когда доработают поддержку пользовательских атрибутов:

в C++
class [[rest_controller]] employee_controller {
public:
    void set_component(std::shared_ptr<employee_repository> repository) {
        _repository = std::move(repository);
    }

    [[get_mapping("/employees")]]
    std::vector<employee> all() {
        return _repository.find_all();
    }

    [[post_mapping("/employees")]]
    employee new_employee([[request_body]] employee new_employee) {
        return _repository.save(std::move(new_employee));
    }

    [[get_mapping("/employees/{id}")]]
    employee all([[path_variable]] size_t id) {
        std::optional<employee> e = _repository.find_by_id(id);
        if (e.has_value()) {
            return std::move(*e);
        }
        throw employee_not_found_exception(id);
    }

    [[delete_mapping("/employees/{id}")]]
    void delete_employee([[path_variable]] size_t id) {
        _repository.delete_by_id(id);
    }

private:
    std::shared_ptr<employee_repository> _repository;
};

В C++ типы всех значений вычисляются в процессе компиляции и жестко фиксированы. "Тип" auto это просто синтаксический сахар, все равно это будет какой-то реальный фиксированный тип.

Все переменные/объекты занимают какое-то количество байт на стеке, и это количество нужно знать заранее, иначе, например, непонятно как вычислить размер stack frame у функции (ассемблеру нужно это знать).

Вот такого в C++ не будет скорее всего никогда:

auto list = create_list();
// ^^^ тип list вычислится в РАНТАЙМЕ,
// будет std::vector<int>/std::vector<std::string>/другой

Аналог того, что происходит в C#/Java, на C++ выглядит скорее как использование std::any, который аллоцирует нужное количество байт для объекта в куче (в отличие от стека, там необязательно знать объем выделяемой памяти в compile-time). Можно иметь один из типов в рантайме:

std::any create_list(int n) {
    if (n == 0) {
        return std::vector<int>();
    } else if (n == 1) {
        return std::vector<std::string>();
    } else {
        return std::vector<char>();
    }
}

int main() {
    int n;
    std::cin >> n;

    auto list = create_list(n);

    if (list.type() == typeid(std::vector<int>)) {
        std::cout << "got vector of int" << std::endl;
    } else if (list.type() == typeid(std::vector<std::string>)) {
        std::cout << "got vector of string" << std::endl;
    } else if (list.type() == typeid(std::vector<char>)) {
        std::cout << "got vector of char" << std::endl;
    }
}

Ради справедливости, высокочастотный трейдинг это не совсем "типичная" C++ программа.

Если, например, писать реалтаймовую программу по обработке аудио-сигналов (где тоже своя атмосфера), то мы связываем себе руки в направлении:

  • не блокировать поток (не пытаться завладеть мьютексом, память аллоцировать строго на стеке, не делать I/O - в общем, не делать системных вызовов)

  • не пользоваться алгоритмами сложности >O(1) / амортизированной сложности

  • по возможности использовать статический полиморфизм вместо динамического

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

То, что сортировка станет вдвое быстрее для 99.9% остальных программ - это же круто? В больших корпорациях ускорение на 2-3% это огромные деньги.

  • Пиши в хидерах код а-ля using namespace std; - коллеги скажут спасибо, за то что им не придется это писать в каждом .cpp!

  • Подключай как можно больше хидеров, чтобы каждый .cpp файл раскрывался в 1mln строк - коллеги скажут спасибо за то, что у них больше времени на перекур во время пересборки!

  • Функция должна вернуть больше чем одно значение? Есть крутой лайфхак - записывай результаты в указатель! Для трёх значений это int process(int arg, int* res2, int* res3).

У меня возник похожий вопрос - а сами студенты перед поступлением в распоряжение к топикстартеру в курсе о гарантированном no hire в Huawei/Intel/Nvidia/etc.?

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

Откуда такой миф, что заниматься "наукой" это нереально здорово, почти как быть сверхчеловеком?

Давайте представим - вы прямо сегодня изобрели лекарство от рака (или что-то подобное, иначе это не "наука", а просиживание штанов) и опубликовались в скопусе.

Вы теперь миллиардер? Нет, все сливки снимет братва с фармкомпаний и академическая мафия, а ты как ехал на маршрутке, так и будешь ехать дальше, ведь у тебя !ПРИЗВАНИЕ! работать за 14-20к.

Про студентов: а кто постановил, что они обязаны работать в науке, особенно под вами? От школьников в 10-11 классах ведь не ждут, что они будут вести уроки за учителей. Так и студенты - они идут в вуз ввиду отсутствия альтернатив (у 95% элементарно откосить от армии), а там у какой-то деловой колбасы на них "виды" в своих корыстных целях. Учитесь работать в рыночной экономике.

С уважением, ваш Фрай

У нас некоторые преподы сходили с ума и присылали какой-то бред

Правила сдачи одного из экзаменов

Скрипт хороший. Жаль только, что многие преподаватели (в основном еще видевшие живого Ленина) ОЧЕНЬ волнуются, если кто-то не слушает именно их пересказ учебника в 8:45, поэтому любят просить включать камеры, задавать рандомные вопросы рандомным людям в рандомное время, и так далее.

Было бы неплохо "из коробки" иметь возможность часть нового кода писать на Rust и связывать его с плюсами. C++ может использовать C, в котором другая система линковки, через extern "C". Почему бы не поддержать в компиляторах extern "Rust" ? Это дало бы буст к юзабельности Rust в реальных проектах и все вышеназванные проекты можно было бы перевезти постепенно.

В той же паре Java/Kotlin можно совершенно бесшовно вызывать код одного языка из другого языка.

Вы имеете в виду String и &str? Это практически аналоги std::string и std::string_view соответственно из плюсов, с такими же сферами применения. У этих двух типов сферы использования пересекаются, но есть случаи когда конкретный тип нужнее. В C++ и в Rust оба типа используются постоянно.

Извините, но вы оба считаете что-то не то...

Это первый вариант (оптимизированный):

std::vector<int> result(NumOfValues);
for (много раз) {
	result.resize(NumOfValues);
	std::iota(out.begin(), out.end(), 1);
}

Это второй вариант (оптимизированный):

for (много раз) {
	std::vector<int> out(NumOfValues);
	std::iota(out.begin(), out.end(), 1);
}

Вызов resize не влияет совсем, так как размер вектора уже достаточный. Но во втором коде лишняя аллокация и деаллокация памяти, из-за чего второй вариант "типа" медленнее. Я поменял одну строку, результат эквивалентный: https://quick-bench.com/q/fSU3z97n18YhNGBgHmojrtW0Y0U

Информация

В рейтинге
Не участвует
Откуда
Ян де нова о-ва
Зарегистрирован
Активность