Coder Social home page Coder Social logo

cpp-ru / ideas Goto Github PK

View Code? Open in Web Editor NEW
89.0 22.0 0.0 12 KB

Идеи по улучшению языка C++ для обсуждения

Home Page: https://cpp-ru.github.io/proposals

License: Creative Commons Zero v1.0 Universal

cpp cplusplus proposals ideas discussion standardization standardisation russian-language helper helpers

ideas's Introduction

Идеи по улучшению языка C++

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

Мы, Рабочая Группа 21, всячески помогаем в проработке идей и их представлении международному комитету ISO WG21 C++.

Популярные идеи, со временем, становятся частью стандарта языка C++.

ideas's People

Contributors

apolukhin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ideas's Issues

Вывод std::chrono с точностью до наносекунд

Перенос предложения: голоса +6, -0
Автор идеи: NN

Решается сегодня через стороннюю библиотеку, хотя должно быть частью стандарта.

https://github.com/HowardHinnant/date/blob/master/include/date/date.h

#include <iostream>
#include <string>

using namespace std;

template <class Precision>
string getISOCurrentTimestamp()
{
    auto now = chrono::system_clock::now();
    return date::format("%FT%TZ", date::floor<Precision>(now));
}

int main() {

 cout << getISOCurrentTimestamp<chrono::nanoseconds>();   
}

Принять в стандарт RAII класс для работы с динамическими библиотеками

Перенос предложения: голоса +43, -2
Автор идеи: Антон Полухин @apolukhin

Предлагаю стандартизировать Boost.DLL, чтобы можно было с лёгкостью делать плагины и импортировать функции по мере необходимости из динамических библиотек:

auto cpp11_func = dll::import<int(std::string&&)>(
    "libmy.so", "i_am_a_cpp11_function"
);

cpp11_func("'Hello word' that is sent to shared library");

Предложение уже доступно по адресу: P0275

Приветствуются любые предложения по улучшению proposal или Boost.DLL!

Boost.Intrusive

Перенос предложения: голоса +9, -1
Aвтор идеи: Александр Зайцев @zamazan4ik

На данный момент мы имеем в стандартной библиотеке неинтурзивные контейнеры. В некоторых операциях, где нам важна производительность(избежать допольнительных аллокаций памяти, noexcept гарантия, и т.д.) мы можем использовать интрузивные контейнеры. Очень хорошая имплементация имеется в Boost.

https://www.boost.org/libs/intrusive

Именованные параметры функции

Перенос предложения: голоса +35, -8
Aвтор идеи: smikler

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

Такая функциональность есть, например, в языке Python.

enum explicit

Перенос предложения: голоса +8, -1
Автор идеи: Pavel

В C++17 можно инициализировать enum значением которого в нём не объявлено:

enum class Handle { Invalid };
Handle h { 42 }; // OK as of C++17

Как бы получается, что в функции/методе, где нужет тот же Handle надо делать обработку невалидных значений, что уже исключительная ситуация. Может всё-таки стоит сделать enum, который никуда не кастится и не преобразуется. Получается такие недотип или полукласс с известными значениями. То есть существуют значения только явно объявленные в перечислении и никакие больше. Синтаксис легко сделать как enum explicit - благо смысл передаёт точно.

В объяснении на cppreference пришут, что это позволяет всякие удобности на каких-то ABI, но такой безопасный enum тоже нужен. Сразу пропадает необходимость проверки валидности. Ну и это в принципе в канве type safety.

Обернуть аргументы программы в С++ классы

Перенос предложения: голоса +49, -0
Автор идеи: Дмитрий Ковальков

Передавать строки как указатель на char и массивы как размер + указатель на начало уже выглядит устаревшим и противоречит core guidelines.

Сейчас:

int main(int argc, char **argv)

Хочется:

int main(std::span<std::string_view> args)

Сразу будут доступны итераторы/удобное сравнение/substr и т.д.

Boost.Sort

Перенос предложения: голоса +4, -5
Aвтор идеи: Александр Зайцев @zamazan4ik

Данная библиотека имеет сортировки, которые выигрывают по скорости во многих случаях у std::sort. Также в библиотеке, возможно, позже появится имплементация TimSort, который уже есть в Python и OpenJDK.

Более подробная информация: http://www.boost.org/doc/libs/1_62_0/libs/sort/doc/html/index.html

Дедукция вывода типа для auto

Перенос предложения: голоса +0, -7
Автор идеи: Максим Некипелов

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

Как это работает сейчас:

template<class T>
struct Property
{
    operator T() const { return { }; }
};

int main()
{
    auto value = Property<int>{ }; // auto будет иметь тип Property<int>
}

Как предлагаю сделать:

template<class T>
struct Property
{
    operator T() const { { }; }
};

template<class T>
auto_deduction Property<T> = T;

int main()
{
    auto value = Property<int>{ }; // будет иметь тип int
}

Также для этого дела не помешала перегрузка:

template<class T>
struct Property
{
    operator T() const { { }; }
};

template<class T>
auto_deduction Property<T> = T;

auto_deduction Property<float> = double;

int main()
{
    auto value = Property<float>{ }; // будет иметь тип double
}

Ну и аналогично поддержку концептов из C++ 20.

Безопасный аналог printf

Перенос предложения: голоса +16, -0
Aвтор идеи: Александр Зайцев @zamazan4ik

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

Например, вот это: https://github.com/fmtlib/fmt

Оператор redirect вместо перегрузки точки

Перенос предложения: голоса +0, -10
Автор идеи: Максим Некипелов

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

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

Предлагаю добавить новый оператор redirect, который будет переадресовывать поле на другой объект, если компилятор его не нашел в действующем. Пример:

#include <iostream>

struct Vector
{
    float X = 0.f;
};

template<class T>
struct Property
{
    T Get() const { { }; }

    redirect Get;
};

int main()
{
    Property<Vector> property;
    std::cout << property.X << std::endl; // поле X не было найдено внутри Property<Vector>, поэтому компилятор начинает его искать в Vector, если находит неявно преображает обращение в property.Get().X
}

На получение указателя поля:

&Property<Vector>::X

Это распространяться не должно. Будет выдаваться ошибка.

std::(forward_)list::extract/insert

Перенос предложения: голоса +0, -3
Автор идеи: Antervis

Идея: добавить к std::list и std::forward_list пары методов extract/insert по аналогии с ассоциативными контейнерами

Парсер json

Перенос предложения: голоса +19, -8
Aвтор идеи: Андрей Урусов

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

explicit using declaration

Перенос предложения: голоса +7, -3
Автор идеи: Nikolay Rapotkin @Rapotkinnik

Добавить модификатор explicit к using объявлению, который бы делал объявляемый тип не просто alias'ом, а прям новым типом c explicit конструктором он исходного типа. Под капотом может быть использован, например, tag dispatching как предлагают тут https://github.com/joboccara/NamedType.

explicit using Uid = std::string;

void foo(Uid uid) {
  // ...
};

foo("Hello"); // wrong
foo(Uid{"Hello"}); // ok

Библиотека для Unit-тестирования

Перенос предложения: голоса +22, -5
Aвтор идеи: Алексей Горностаев

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

Известные библиотеки:
https://github.com/google/googletest
http://www.boost.org/doc/libs/1_62_0/libs/test/doc/html/index.html
https://msdn.microsoft.com/en-us/library/hh598953.aspx
https://github.com/google/googletest

Расширить static_cast для nested class

Перенос предложения: голоса +0, -7
Автор идеи: Максим Некипелов

Дать возможность статически преобразовывать указатели вложенных классов на указатель держателя (вычислять смещение поля и отбавлять его от this). Конечно же для шаблона и если компилятор увидел связь.

Дать возможность данному примеру компилироваться:

template<class T>
class CWheel
{
public:
	void Turn()
	{
		static_cast<T*>(this)->Velocity += 10.f; // сейчас тут ошибка
	}
};

class CVehicle
{
public:
	float Velocity = 0.f;

	CWheel<CVehicle> Wheel;
};

template<class T>
void ProcessWheel(CWheel<T>* wheel)
{
	wheel->Turn();
}

int main()
{
	CVehicle car;

	ProcessWheel(&car.Wheel);
}

std::cyclic_iterator

Перенос предложения: голоса +7, -1
Автор идеи: Денис Черников

Добавить обёртку над контейнерами, итераторами или диапазонами, которая по достижении конца автоматически вернётся в начало. Полезно для стандартных алгоритмов, в которых у (одной из) последовательностей не требуется указывать конец (количество итераций зависит от другого диапазона). Gjktpyj lkz

Набросок на gist.github.com

Атрибут [[visible]], упрощающий создание динамических библиотек.

Перенос предложения: голоса +44, -3
Aвтор идеи: Антон Полухин @apolukhin

Наверное многие сталкивались с ситуацией, что чтобы создать динамическую биюлиотеку или плагин, необходимо написать подобный код:

    #if EXPORTING
    #   if MSVC
    #       define API __declspec(dllexport)
    #   else
    #       define API __attribute__((visibility("default")))
    #   endif
    #else
    #   if MSVC
    #       define API __declspec(dllimport)
    #   else
    #       define API
    #   endif
    #endif

После чего, все публичные методы библиотеки пометить описанным выше макросом API:

    // Public interface
    API bool grph_is_tree(graph* g);
    API bool grph_is_directed(graph* g);

В дальнешем нужно следить за параметрами сборки, правильно задавать макросы EXPORTING и возможно придется дорабатывать код (чтобы можно было собирать статические библиотеки с тем же заголовочным файлом, собираться на других платформах и т.п.)

Так вот, предлагаю сделать атрибут [[visible]], который берёт эту задачу на себя. Предложение уже доступно по адресу https://wg21.link/p0276r0

С радостью выслушаю любые идеи и замечания к предложению!

Подмешивание новых методов уже существующим классам

Перенос предложения: голоса +3, -7
Автор идеи: valera_ee

Думаю, многие согласятся, что подход STL не очень удобен в использовании, когда алгоритмы живут сами по себе, а типы данных, к которым применяются эти алгоритмы, сами по себе. Не всегда очевидно какой алгоритм можно или нельзя использовать с тем или иным контейнером. Но STL уже не изменить, но можно улучшить.
На примере класса std::vector покажу, как я сделал его немного удобнее для себя.

Есть шаблонный класс, которы реализует некоторый алгоритм, например, заполнение контейнера некоторым значением:

template <class Container>
class add_fill
{
public:
    template<class Arg>
    Container& fill(Arg &&arg) {
        auto p = static_cast<Container*>(this);
        std::fill(p->begin(), p->end(), arg);
        return (*p);
    }
};

Теперь конструирую свой вектор на основе std:: vector :

template <class T>
class my_vector : public std::vector<T>,
                  public add_fill<vector<T>>
{
public:
    template <class ...Args>
    vector(Args&& ...args) : std::vector<T>(args...) {}
    vector(std::initializer_list<T> init) : std::vector<T>(init) {}
};
Таким образом можно добавить множество алгоритмов и оно работает, но есть минус, я вынужден создавать новый тип через наследование.

Куда удобнее написать что-то вроде:

template<class T>
using my_vector_t = std::vector<T, add_fill, add_copy, add_find, add_sort, add_transform>;

my_vector_t<int> my_vec;
my_vec.resize(N);
my_vec.fill(-200_dB);
my_vec.transform_from(other_vec, [](int val){ return 10*val - 5; });
А для ещё большего удобства делать возвращаемый тип не void, а ссылку на себя, в таком случае можно писать так:

my_vec.resize(N).fill(-200_dB).transform_from(other_vec, [](int val){ 
    return 10*val - 5; 
});

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

Стандартизация SIMD инструкций

Перенос предложения: голоса +2, -2
Автор идеи: Игорь Буренков

Для использования SSE и AVX инструкций мы подключаем библиотеки:

#include "xmmintrin.h" или #include <x86intrin.h>
Видите? Уже здесь наблюдается нестыковка в виде множества библиотек, часть из которых проприетарна.

Рассмотрим пример SSE инструкции:

#include "iostream"
#include "xmmintrin.h"	

int main()
{
	const auto N = 8;

	alignas(16) float a[] = { 41982.0,  81.5091, 3.14, 42.666, 54776.45, 342.4556, 6756.2344, 4563.789 };
	alignas(16) float b[] = { 85989.111,  156.5091, 3.14, 42.666, 1006.45, 9999.4546, 0.2344, 7893.789 };
	
	__m128* a_simd = reinterpret_cast<__m128*>(a);
	__m128* b_simd = reinterpret_cast<__m128*>(b);

        auto size = sizeof(float);
        void *ptr = _aligned_malloc(N * size, 32);
	float* c = reinterpret_cast<float*>(ptr);
	
        for (size_t i = 0; i < N/2; i++, a_simd++, b_simd++, c += 4)
		_mm_store_ps(c, _mm_add_ps(*a_simd, *b_simd));
	c -= N;

	std::cout.precision(10);
	for (size_t i = 0; i < N; i++)
		std::cout << c[i] << std::endl;

	_aligned_free(ptr);

	system("PAUSE");
	return 0;
}

Мало того, что этот код выглядит мало понятно из-за всяких __m128 и т.д., так программа, скомпилированная с SIMD не будет работать в системах без его.

А, возращаясь к вопросам производительности на ядро - AVX и SSE позволяет ускорить производительность на ядро от 8 (SSE) до 16 (AVX) раз.
Также стоит обратить внимание, что раньше монополистом данной технологии была Intel и SIMD использовалась исключительно в серверных процессорах, однако сейчас SIMD можно встретить и в потребительском сегменте, а также у компании-конкурента AMD.

Это показывает важность интеграции новой технологии, что поддерживается практически всеми современными CPU x86. Причем интеграция не должна останавливаться на ассемблерообразном коде, приведя код к удобному и практичному виду. Также важно добавить провеки наличия SIMD и возможность использования SIMD объектов на процессорах без онных инструкций, пусть и с потерей производительности. Это позволит ускорить современное ПО без потери обратной совместимости, а также ускорить разработку и дальнейшую поддержку кода с использованием SIMD инструкций.

Функции преобразования строки в число должны поддерживать работу с итераторами

Перенос предложения: голоса +6, -0
Автор идеи: waka.packmaca

Функции std::stoi, std::stol, std::stoll и т.д. принимают std::string/std::wstring в качестве аргумента, что делает их "бесполезными" при работе с диапазонами.

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

template<typename Iterator>
int stoi(Iterator first, Iterator last, int base = 10) {
  // ...
}

Добавить возможности кортежей к агрегатам

Перенос предложения: голоса +6, -2
Автор идеи: Антон Полухин @apolukhin

std::tuple и std::pair отлично подходяд для обобщённого программирования, однако у них есть недостатки. Во первых, использующий их код сложно читать и понимать:

struct auth_info_aggreagte {
    std::int64_t id;
    std::int64_t session_id;
    std::int64_t source_id;
    std::time_t valid_till;
};

using auth_info_tuple = std::tuple<
    std::int64_t,
    std::int64_t,
    std::int64_t,
    std::time_t
>;

Объявление через структуру намного понятнее, видно за что какое поле отвечает, что в нём хранится. Использование структур тоже более понятное: return std::get<1>(value); сравните с return value.session_id;

Во вторых, агрегаты предоставляют более эффективные операции копирования, присваивания и перемещения:

template <class T>
constexpr bool validate() {
    static_assert(std::is_trivially_move_constructible_v<T>);
    static_assert(std::is_trivially_copy_constructible_v<T>);
    static_assert(std::is_trivially_move_assignable_v<T>);
    static_assert(std::is_trivially_copy_assignable_v<T>);
    return true;
}

constexpr bool tuples_fail = validate<auth_info_tuple>(); // Проваливает большинство проверок
constexpr bool aggregates_are_ok = validate<auth_info_aggreagte>();

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

Однако, мы не можем использовать агрегаты в обобщённом коде (разве что мы воспользуемся библиотекой Boost.PFR):

namespace impl {
    template <class Stream, class Result, std::size_t... I>
    void fill_fileds(Stream& s, Result& res, std::index_sequence<I...>) {
        (s >> ... >> std::get<I>(res));
    }
}

template <class T>
T ExecuteSQL(std::string_view statement) {
    std::stringstream stream;
    // some logic that feeds data into stream

    T result;
    impl::fill_fileds(stream, result, std::make_index_sequence<std::tuple_size_v<T>>());
    return result;
}

constexpr std::string_view query = "SELECT id, session_id, source_id, valid_till FROM auth";
const auto tuple_result = ExecuteSQL<auth_info_tuple>(query);
const auto aggreagate_result = ExecuteSQL<auth_info_aggreagte>(query); // does not compile

// Можно проверить на https://godbolt.org/z/y49lya

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

Поддержка "человеческой" инициализации полей структуры, битовых полей и прочих именованных полей везде где возможно

Перенос предложения: голоса +1, -5
Автор идеи: HedgehogInTheCPP @IRainman

Компилятору никто не мешает принять на вход инициализации поля структуры по имени (id) без явного написания кучи blow кода в виде трёх этажных шаблонов.

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

static volatile uint16_t* const name = (volatile uint16_t*)(XXXXX);

struct ConfigDacLoop
{
   uint16_t unsign : 1, loop : 1;
};


ConfigDacLoop get_config_dac_loop() { return static_cast<ConfigDacLoop>(*config_dac_loop); }

void set_config_dac_loop(const ConfigDacLoop& p_config_dac_loop) { *config_dac_loop = p_config_dac_loop; }


set_config_dac_loop(.unsign = 1, .loop = 0); // Not working.

set_config_dac_loop({.unsign = 1, .loop = 0}); // Not working.

set_config_dac_loop(ConfigDacLoop{.unsign = 1, .loop = 0}); // Not working.​

Ослабить требование к второму аргументу в static_assert

Перенос предложения: голоса +43, -0
Автор идеи: Александр Караев @Smertig

Текущая версия стандарта требует, чтобы второй аргумент static_assert'а являлся string literal, что не позволяет использовать в нём constexpr char*

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

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

Представим старый код:

#include <type_traits>
#include <string>

template <class... Args>
class foo {
  static_assert(std::conjunction_v<std::is_pod<Args>...>, "All args must be pod!");
};

int main() {
	foo<int, std::string, float> f;
}

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

А теперь новый код, если предложение примут:

template <class... Args>
class foo {
  static_assert(std::conjunction_v<std::is_pod<Args>...>, compile_time_joiner("All args must be pod, but ", type_name<find_non_pod<Args...>>, " isn't"));
};

Преимущество очевидно. На данный момент существует несколько реализаций compile-time строк, множество алгоритмов для работы с типами, поэтому реализация compile_time_joiner, type_name<..> и find_non_pod<...> - не проблема. С учётом того, что идёт активная работа над рефлексией, то упростится и работа с типами. Там подоспеет и аналог sprintf времени компиляции. А раз у нас будет более удобное метапрограммирование, значит нужны и информативные сообщения об ошибках!

Возможность realloc

Перенос предложения: голоса +8, -1
Aвтор идеи: h4tred

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

Примерный вид может быть таким:

new_ptr = new (old_ptr) Foo[new_size];

Сигнатура для оператора new, может быть такой:

void* operator new[] (size_t size, void *old_ptr, struct realloc_tag);

тег нужен, что бы не конфликтовать с существующей сигнатурой для placement new.

Предложенный синтаксис в текущем виде применим для placement new, но может вызывать ряд проблем:

Если кто-то осторожно работает с placement new для массивов, возможна поломка обратной совместимости. Что бы избежать возможно:

Добавление тега в вызов new:
    new (realloc_tag, old_ptr) Foo[new_size]​
Использовать новый специальный оператор, напрример renew

Данных механизм применим только для памяти выделенной при помощи new[].

Поддержка со стороны языка нужна, что бы можно было отличить тип элемента массива и правильно принять решение: делать realloc или вызывать new[]+move/copy+delete[].

Добавить возможность миксинов в язык С++

Перенос предложения: голоса +1, -5
Автор идеи: Мартынов Иван @VanyaClassicTGN

Доброго времени суток, друзья.

сейчас миксины добавляются следующийм образом

template <class T>
class B {}

class Derived : public B<Derived> {}

заменить на

class Derived : public mixin B {}

С уважением, Мартынов Иван.

PIMPL or FastPIMPL delete

Перенос предложения: голоса +10, -0
Автор идеи: Мартынов Иван @VanyaClassicTGN

Доброго времени суток!

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

С уважением, Мартынов Иван.

explicit assignment operator

Перенос предложения: голоса +12, -2
Автор идеи: Топунов Владимир Андреевич

Подобно explicit конструктору запрещает неявное преобразование типа при присваивании

std::string s;
s = 49; // Неявное преобразование int в char
std::cout << s << std::endl; // Вывод символа '1' 
class String
{
public:
    explicit operator = (char ch);
...
};

...

String s;
s = 49; // Ошибка компиляции
s = '1'; // OK

constexpr std::regex

Перенос предложения: голоса +45, -1
Автор идеи: Антон Полухин @apolukhin

Множество языков программирования в данный момент компилируют/транслируют регулярные выражения ещё перед запуском программы. Таким образом, когда программа стартует, все регулярные выражения уже преобразованы в соптимизированный конечный автомат.

Предлагаю добавить подобную возможность и для C++.

bool is_valid_mail(std::string_view mail) {
    static const std::regex mail_regex(R"((?:(?:[^<>()\[\].,;:\s@\"]+(?:\.[^<>()\[\].,;:\s@\"]+)*)|\".+\")@(?:(?:[^<>()\[\].,;:\s@\"]+\.)+[^<>()\[\].,;:\s@\"]{2,}))");

    return std::regex_match(
        std::cbegin(mail),
        std::cend(mail),
        mail_regex
    );
}

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

С готовящимися новинками для constexpr вычислений (constexpr new, is_constexpr_evaluated() и др.) можно будет в C++20 делать множество вещей на этапе компиляции, в том числе можно будет сделать constexpr std::regex.

С constexpr std::regex конечный автомат для функции is_valid_mail() построится ещё на этапе компиляции. Более того, GCC сможет генерировать оптимизированные регулярки на этапе компиляции без static const, т.к. начиная с GCC-6 если у constexpr функции все параметры на вход — константы, GCC форсирует вычисление на этапе компиляции. Багрепорт на добавление аналогично функционала в Clang уже есть

Конструктор для string_view, хранящий длину литерала

Перенос предложения: голоса +1, -4
Автор идеи: Денис Черников

В некоторых случаях мог бы быть полезен шаблонный конструктор std::string_view (с шаблонным параметром std::size_t N), принимающий массив CharT и инициализирующий длину контейнера равной шаблонному N.

P.S.: не проверял, будет ли это конфликтовать с текущими перегрузками конструктора.

Boost.Bimap

Перенос предложения: голоса +8, -3
Aвтор идеи: Александр Зайцев @zamazan4ik

В отличие от std::map, где ключом у Вас может быть только одно значение, в Boost.Bimap ключом у Вас может быть каждое из значений, что довольно удобно. Было бы неплохо добавить это в стандарт.

Обертка над стандартными типами данных(char, int, double и т.д.)

Перенос предложения: голоса +2, -5
Автор идеи: Nikola_g

Суть проблемы заключается в следующим. В компании, в которой я работаю в основном решаются математические задачи и часто при их решении возникают непредвиденные ситуации. К ним относятся деления на ноль, результат вычисления равен inf или nan, бывает, что происходит переполнения. Так же по незнанию программисты берут на строгое равенство переменные с плавающий точкой. Для решении данной проблемы были написаны несколько классов (ссылка на репозитарий https://github.com/GhostPastR/TestMyLibs/tree/master/libs/system/NType). Возможно ли внести в стандартную библиотеку что-нибудь аналогичное. В C# и Java стандартные типы оформлены виде классов со своими методами.

Модификация string.replace

Перенос предложения: голоса +18, -0
Автор идеи: Иван Володин

Модификация string.replace для замены символов или строк на другие символы или строки

В .NET есть функция String.Replace, имеющая перегрузки String.Replace(char oldValue, char newValue) и String.Replace(string oldValue, string newValue) для замены в строке всех oldValue на newValue.

В C++ тоже есть похожая функция, string.replace(size_t start, size_t count, const string &str), однако она позволяет только заменить count символов строки с позиции start из другой переданной строки str.
Если есть необходимость замены во всей строке одного символа другим, или замены одной подстрокой другой, то придется использовать regex или писать самопальные функции.

Почему бы не расширить string.replace(size_t start, size_t count, const string &str) до string.replace(char oldValue, charnewValue) и string.replace(string oldValue, string newValue)?

Классы для получения backtrace

Перенос предложения: голоса +36, -6
Aвтор идеи: Антон Полухин @apolukhin

На данный момент в С++ нет изкоробочного метода для получения стека вызовов и работы с ним. Предлагаю придумать такие методы и классы, написать прототип и сделать егостандартом языка.

На данный момент есть прототип, который активно мной дописывается stacktrace:

std::stacktrace s;
std::cout << s;     // выведет стектрейс

std::cout << s[0];  // выведет текущую функцию, например:
// foo(int)    ../example/getting_started.cpp:29

Пожалуста, расскажите чего вам не хватает в прототипе и что вам кажется в нём неправильным.

Добавить пару комбинаторных функций

Перенос предложения: голоса +4, -1
Автор идеи: Ayrtat

Зачастую в проектном коде применяются эвристические алгоритмы, подразумевающие какой-нибудь перебор чисел или объектов в контейнере.
В данном случае речь идет о генерации всех перестановок с повторениями (permutation with repetition) или же сочетаний (combinations).
Кроме того, такие функции очень часто могут пригодится в олимпиадном программировании (хотя, очевидно, от авторов задач предполагается создавать такие функции с нуля, вручную).
Мой поинт - избавить программиста, участвующего в коммерческой разработке:
А) Писать самостоятельно C-style генерации в виде вложенных семиэтажных циклов for;
Б) Разрабатывать полноценный алгоритм, который будет работать только с контейнерами, поддерживающие RandomAccessIterator.
В) Делать код нечитаемым и дублируемым, несочетающимся с другими STL-фукнкциями
Предлагаю навязать разработчику использование стандарта для отличной совместимости с другими STL-функциями, контейнерами и сделать код более читаемым!
Как итог: добавить генерацию перестановок с повторениями (permutation with repetition) и генерацию сочетаний (combinations).

template<typename InputIt, typename T>
bool nextPermutationWithRepetition(InputIt begin, InputIt end, T from_value, T to_value) {
	auto it = std::find_if_not(std::make_reverse_iterator(end),
	std::make_reverse_iterator(begin),
	[&to_value](auto current) { return to_value == current; });

	if (it == std::make_reverse_iterator(begin))
		return false;

	auto bound_element_iterator = std::prev(it.base());

	(*bound_element_iterator)++;
	std::fill(std::next(bound_element_iterator), end, from_value);

	return true;
}

template<typename InputIt, typename T>
bool nextCombination(InputIt begin,
		     InputIt end,
		     T toElement) {
	const auto lengthOfSubsequence = std::distance(begin, end);
	auto pos = lengthOfSubsequence;

	auto viewed_element_it = std::make_reverse_iterator(end);
	auto reversed_begin = std::make_reverse_iterator(begin);

	/*Looking for this element here*/

	while ((viewed_element_it != reversed_begin) && 
               (*viewed_element_it >= toElement - lengthOfSubsequence + std::distance(viewed_element_it, reversed_begin))) 
        { viewed_element_it++; }

	if (viewed_element_it == reversed_begin)
		return false;

	auto it = std::prev(viewed_element_it.base());

	std::iota(it, end, *it + 1); //with C++17

	return true;
}

/* The example of usage */

std::list<int> vec(3);

do {
    std::copy(vec.begin(), vec.end(), std::ostream_iterator<int>(std::cout, " "));
    std::cout << std::endl;
} while (nextPermutationWithRepetition(vec.begin(), vec.end(), 0, 2));
std::list<int> vec = { 1, 2, 3 };

do {
    std::copy(vec.begin(), vec.end(), std::ostream_iterator<int>(std::cout, " "));
    std::cout << std::endl;
} while (nextCombination(vec.begin(), vec.end(), 10));

​
template<typename InputIt, typename T>
bool nextPermutationWithRepetition(InputIt begin, InputIt end, T from_value, T to_value) {
	auto it = std::find_if_not(std::make_reverse_iterator(end),
	std::make_reverse_iterator(begin),
	[&to_value](auto current) { return to_value == current; });

	if (it == std::make_reverse_iterator(begin))
		return false;

	auto bound_element_iterator = std::prev(it.base());

	(*bound_element_iterator)++;
	std::fill(std::next(bound_element_iterator), end, from_value);

	return true;
}

template<typename InputIt, typename T>
bool nextCombination(InputIt begin,
		     InputIt end,
		     T toElement) {
	const auto lengthOfSubsequence = std::distance(begin, end);
	auto pos = lengthOfSubsequence;

	auto viewed_element_it = std::make_reverse_iterator(end);
	auto reversed_begin = std::make_reverse_iterator(begin);

	/*Looking for this element here*/

	while ((viewed_element_it != reversed_begin) && 
               (*viewed_element_it >= toElement - lengthOfSubsequence + 									  std::distance(viewed_element_it, reversed_begin))) { viewed_element_it++; }

	if (viewed_element_it == reversed_begin)
		return false;

	auto it = std::prev(viewed_element_it.base());

	std::iota(it, end, *it + 1); //with C++17

	return true;
}

/* The example of usage */

std::list<int> vec(3);

do {
    std::copy(vec.begin(), vec.end(), std::ostream_iterator<int>(std::cout, " "));
    std::cout << std::endl;
} while (nextPermutationWithRepetition(vec.begin(), vec.end(), 0, 2));

std::list<int> vec = { 1, 2, 3 };

do {
    std::copy(vec.begin(), vec.end(), std::ostream_iterator<int>(std::cout, " "));
    std::cout << std::endl;
} while (nextCombination(vec.begin(), vec.end(), 10));

if inline constexpr

Перенос предложения: голоса +3, -8
Автор идеи: Максим Некипелов

Возникает потребность расширить область видимости какого-то объекта за приделы скопа if constexpr. Наглядно в очень простом примере:

template<bool Input>
void OutHello()
{
	if constexpr (Input)
	{
		int value = 0;
		std::cin >> value;
	}

	std::cout << "HELLO!" << std::endl;

	if constexpr (Input)
	{
		std::cout << "input " << value << std::endl; // не можем использовать value
	}
}

Код приходится писать два раза для реализации этой логики.

template<bool Input>
void OutHello()
{
	if constexpr (Input)
	{
		int value = 0;
		std::cin >> value;

		std::cout << "HELLO!" << std::endl;

		std::cout << "input " << value << std::endl; // можем использовать value
	}
	else
		std::cout << "HELLO!" << std::endl; // ПОВТОР!
}

Не очень приятное занятие. Особенно когда промежуток занимает значительное количество строк.

Предлагаю добавить конструкцию if inline constexpr, которая не будет ограничивать область видимости объявленных в нем объектов. Проще говоря просто подставлять код (как #if #endif). Пример:

template<bool Input>
void OutHello()
{
	if inline constexpr (Input)
	{
		int value = 0;
		std::cin >> value;
	}

	std::cout << "HELLO!" << std::endl;

	if inline constexpr (Input)
	{
		std::cout << "input " << value << std::endl; // ВСЕ ОК!
	}
}

Нужно понимать, что пример взят очень простой. И заранее отвечаю на возможно возникшие притензии:

Вынести эту переменную за приделы скопа, компилятор ее все равно вырежет за ненужностью!

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

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

Промежуточный код может сильно зависить от каких-то переменных, которые объявлены выше. И в добавок сам производить переменные, которые будут использоваться ниже. Будет получаться не очень красиво. Лучше просто добавить новый оператор и возложить все обязаности на компилятор.

Метод empty для stringstream

Перенос предложения: голоса +7, -1
Автор идеи: Semyon Sergiev @sergiev

Сейчас, чтобы проверить stringstream на наличие несчитанных символов, требуется использовать следующую конструкцию:
my_stringstream.rdbuf()->in_avail()==0

Почему бы не добавить метод empty, дабы укоротить эту инструкцию до следующей?
my_stringstream.empty()

std::string_view есть, а куда его засунуть — нет

Перенос предложения: голоса +10, -0
Автор идеи: ldvsoft

Надо перебрать все методы, принимающие строчки в качестве аргументов для «посмотреть», потому что не во все из них можно передать std::string_view, хотя казалось бы.

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

  1. Конструктор std::bitset (принимает std::string const & или char const * на пару с позициями начала и конца, это же как раз std::string_view),
  2. std::stol и её друзья,
  3. std::from_chars туда же.
  4. Методы поиска и доступа в std::map и std::unordered_map: сейчас имея на руках std::string_view приходится строить полную std::string лишь бы проверить, лежит ли в мапе нужное значение, что точно избыточно. Оно вполне может заслуживать отдельного пропозала на гетерогенный поиск в контейнерах, подобных множествам (std::set туда же). Понятно, что универсальный код контейнеров вряд ли может рассчитывать, чтобы компаратор/хешер строчек умел принимать дополнительные типы, но очень уж неудобно получается. Неявное приведение std::string_view→std::string решило бы проблему, но мы понимаем, что так делать скорее не надо. Всю эту проблему можно решить храня множество каноничных std::string и используя отображения std::string_view на каноничную→Value, но удобство уменьшается. Наверное тут ситуация будет похожая и для случаев, когда в контейнере ищут другой контейнер по range необходимых элементов, но со стороны это точно другая проблема.

Если последний пункт в целом заслуживает абсолютно отдельного разговора (хотя если вдруг мы можем его относительно просто решить то давайте!), то и без него технически мелких вещей хватает, потому что приходится класть std::string_view в переменную и передавать по кускам, а добавить перегрузку не должно ломать ABI. Более того, возможно стоит задуматься об устаревании старых куч перегрузок, все из которых сводятся к передаче аналогов std::string_view, и в будущем дизайнить подобные методы/функции аккуратнее.

Расширить алгоритмы, принимающие значение и бинарный предикат

Перенос предложения: голоса +2, -2
Aвтор идеи: h4tred

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

Например:

  int myints[] = {1,2,3,4,5,4,3,2,1};
  std::vector<int> v(myints,myints+9);                         // 1 2 3 4 5 4 3 2 1

  // using default comparison:
  std::sort (v.begin(), v.end());

  auto it = std::binary_search(v.begin(), v.end(), 4, [](auto lhs, auto rhs) {
    // тут может быть куда более сложная проверка
    return lhs < rhs;
  });

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

Пример с использованием унарного предиката:

  auto it = std::binary_search(v.begin(), v.end(), [](auto value) {
    // тут может быть куда более сложная проверка
    return value < 4;
  });

К подобным алгоритмам относятся:

  • std::binary_search
  • std::lower_bound
  • std::upper_bound
  • std::equal_range
  • std::search_n

Понятна необходимость использования общего компаратора с std::sort (или другими), ведь работа алгоритмов бинарного поиска основана на условии сортировки. С другой стороны, часто для сортировки сложных структур используется одно-два поля и искать запись только по этому полю становится неудобно: нужно передавать целый объект, что бы передать только одно/два поля. В случае сортировки эти объекты уже есть, в случае поиска - его нужно создать и заполнить самому.

Making all std::char_traits functions constexpr

Перенос предложения: голоса +3, -0
Aвтор идеи: Anton Bikineev

Идея сделать только 4 функции константными выражениями выглядит как ад-хок решение для string_view быть константным выражением. Почему некоторые функции остались без внимания? Что, если они будут нужны потом? Что вы скажете своим детям? Вы или они будете писать дефект репорт и еще один пропозал?

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

  • DR2777:basic_string_view::copy should use char_traits::copy
  • DR2778:basic_string_view is missing constexpr

Первый репорт обязывает сделать basic_string_view::copy через char_traits::copy, а второй репорт, в частности, предлагает сделать basic_string_view::copy констэкспр. Но как вы сделаете the latter констэкспр без the former констэкспр?

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

Библиотека для логирования

Перенос предложения: голоса +13, -4
Aвтор идеи: Александр Зайцев @zamazan4ik

На данный момент в C++ крайне слабая система логирования - clog ни на что не годится. Было бы неплохо иметь в библиотеке более мощную библиотеку логирования. Варианты: Boost.Log, spdlog, log4cplus, pantheios, easyloggingpp. Быть может, что-то другое.

http://www.boost.org/doc/libs/1_62_0/libs/log/doc/html/index.html

https://github.com/gabime/spdlog

https://github.com/easylogging/easyloggingpp

http://www.pantheios.org/

https://github.com/log4cplus/log4cplus

Методы iterator_from

Перенос предложения: голоса +6, -0
Автор идеи: Antervis

Идея: добавить способ получения итераторов из ссылок/указателей на элементы контейнера, вида

template <container T>
T::iterator iterator_from(T::value_type& val);

template <container T>
T::const_iterator iterator_from(const T::value_type& val);

template <associative_container T>
T::iterator iterator_from_key(const T::key_type& val);

template <associative_container T>
T::iterator iterator_from_mapped(T::mapped_type& val);

template <associative_container T>
T::const_iterator iterator_from_key(const T::mapped_type& val);

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

Такие методы позволили бы некоторые сценарии работы с обычными контейнерами, которые ранее были доступны только интрузивным.

Интерфейсы(с виртуальными и невиртуальными методами)

Перенос предложения: голоса +1, -5
Автор идеи: Виталий Псевдокенко

Интерфейсы в общем нужны для избежания ошибок при написания самого тела класса(то есть как можно раньше, а не при передаче объекта как аргумента функции или вообще багов на рантайме в случае использование множественного наследования), а также для подсказок IDE и автогенерации кода. Виртуальные интерфейсы для избежания проблем с множественным наследованием. Невиртуальные интерфейсы наподобие концептов в с++20, а также как их дополнение.

1 Интерфейсы в целом.

1.1 Для объявления интерфейса используется контекстное слово “interface” вместе с ключевым словом “class”

class interface MyInterface{
    // *** 
};

1.2 Объявления реализации интерфейса осуществляется так же как и при наследовании класса от другого класса, только без указания модификатора доступа наследования.

class interface MyInterface: OtherInterface1, OtherInterface2{
    // *** 
};

1.3 При реализации интерфейса запрещается множественное наследование от классов(целесообразность этого пункта под вопросом)

1.4 Интерфейс может использоваться так же как и концепт (под капотом компилятор создаёт концепт без имени с проверкой std::is_base_of):

template<MyInterface T>
void func(T x);

1.5 Уточнения std::is_base_of для интерфейсов, и/или добавление отдельного библиотечного класса для такой проверки.

1.6 Методы с реализацией по умолчанию:

class interface MyInterface{
    // Метод без реализации
    void test();
    
    // Метод с реализацией по умолчанию вне тела класа или с реализацией компилятора(к 
    // примеру операторы сравнения начиная с c++20
    void test2() = default;

    // Метод с реализацией по умолчанию внутри класа
    void test3() = default {
        // ...
    }
};

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

1.8 Для модификатора доступа “protected” разрешены также методы с реализацией по умолчанию.

1.9 Для модификатора доступа “public” разрешены также методы с и без реализации.

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

1.11 Члены с модификатором “private” используются методами с реализацией по умолчанию.

1.12 Для переопределения метода при колизии используется ключевое слово “using”:

class interface MyInterfaceA{
    int test(int a, int b) = default{
       return a + b;
    }
};

class interface MyInterfaceB {
    int test(int a, int b) = default{
       return a + b;
    }
};

class interface MyInterfaceC: MyInterfaceA, MyInterfaceB {
    using int MyInterfaceB::test(int a, int b);
};

class MyClass: MyInterfaceA, MyInterfaceB {
    using int MyInterfaceA::test(int a, int b);
};

1.13 Модификатор доступа по умолчанию - “public” .

1.14 Определения виртуального или невиртульного метода:

class interface MyInterfaceA{
    void test1();
    virtual void test2();
};

class interface MyInterfaceB: MyInterfaceA{
    void test1() = default{
        // ...
    }

    virtual void test2();
};

class MyClassB: MyInterfaceB{

};

class interface MyInterfaceC: MyInterfaceB{
    virtual void test2() = default{
        // ...
    }
};

class MyClassC: MyInterfaceC{

};

int main(){
   MyInterfaceA * ptr; // ОШИБКА: у интерфейса есть нереализованые невиртуальные методы 
   MyInterfaceB * ptr; // ОК

   MyClassB obj; // ОШИБКА: у интерфейса есть нереализованые виртуальные методы
   MyClassC obj; // ОК

}

2 Невиртуальные методы интерфейса

2.1 Разрешается использования “override”, “final”, “= 0” для невиртуальных методов интерфейса для проверки в классе реализации метода:

class interface MyInterface{
    void test1();

    void test_final();};
class MyClass1{public:
    void test1() override { //...} // ОК
    void test2() override { //...} // ОШИБКА

    void test_final() final override { // ...}}
class MyClass2: public MyClass1{public:
    void test_final() override { // ...} // ОШИБКА
    void test_final() { // ...}          // ОК}
}

2.2 Специальная конструкция для определения типа класса, который реализуюет невиртуальный метод (определяется при каждой реализации, так как невиртуальный метод недоступен по ссылке или указателю интерфейса или класса, который не реализует метод); (используется как синтаксический сахар над CRTP, и в случае переопределения метода):

class interface MyInterface{

    auto test(); // auto - может содержать тип класса, который реализует интерфейс
}

class MyClass1: MyInterface{

     MyClass1 test() override{//...} //ОК

     int test() override{//...} //ОШИБКА 
}

class MyClass2: public MyClass1{

     MyClass2 test() override{//...} //ОК

}​

Работа с сетью

Перенос предложения: голоса +58, -1
Автор идеи: maksimus1210

Любое современное ПО требует работы с сетью, а отсутствие в стандартной библиотеке таких классов заставляет искать альтернативы и переход на другой язык программирования, в языке GO очень развитая библиотека для работы с сетью и если требуется реализовать HTTP сервер, то я зык GO идеально подходит для этого.

Свойства (property)

Перенос предложения: голоса +6, -6
Автор идеи: Максим Некипелов

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

В реализации C++ от Microsoft и от Borland уже можно найти жалкое подобие свойств в виде этого:

struct A
{
	void Set(int value) { }
	int Get() { return 1; }
	__declspec(property(put=Set, get=get)) MyProperty;
};

К минусам:

  1. Сеттер и геттер живут все еще не в свойстве и засоряют пространство имен класса

  2. Невозможно получить указатель на поле (самый главный минус)
    &A::MyProperty // ошибка

  3. Код свойства (геттеров и сеттеров) может повторяться, а менятся только то, с чем взаимодействуем, но его все равно нужно копипастить. Как так? У нас же метапрограммирование

Мотивация использования данного чуда:

Наверное тут мотивация может отсутвовать только у мазохиста. Но может кто-то не понимает область использования свойств, поэтому демонстрация:

Control.SetPosition(Control.GetPosition() + { 100.f, 100.f });
Как видим не очень красиво. А вот так уже красиво (код превращается в то, что выше, только это компилятор генерирует сам):

Control.Position += { 100.f, 100.f };
Но это мелочи по сравнению с тем, что пойдет дальше. У вас есть класс шаблон, который как-то обрабатывает поля у объекта, которые передаются в шаблон, используя операторы =, /, +, -, +=, -= и т.д., а вы туда не можете передать ваш Position, потому что при его изменении нужно вызывать Get и Set, а писать отдельную специализацию шаблона под указатели на методы банально лень. Тут все таки метапрограммирование как никак.

Особенно станет обидно когда в C++23 добавят рефлексию, а у вас тут методами все обмазано. По нормальному это должно все делаться через свойства. Думаю многие из вас видели редакторы и уровней в играх, и контролов для пользовательких приложений и там было окошечко со свойствами какого-то объекта. Задача банальная, а без свойств перерастает в ужастики. Конечно, можно написать свои свойства, но выглядит это все очень колхозно с кучей костылей.

Я предалагю добавить в язык ключевые слова: property, set, set_const, get, get_const, value.

set - для создания скопа кода, который будет вызываться при использовании операторов =, +=, -= и т.д. у свойства.

get - для создания скопа кода, который будет вызываться для приведения свойства в тип данных, которое оно оборачивает, также внутри операторов +=, -= и т.д.

Т.е. свойство при += ведет себя так (псевдокод):

type temp = get();
temp += value;
set(temp);
return temp;

Для +:

return get() + right;

Для других операторов думаю принцип работы понятен.

property также как class может взаимодействовать с template. Объект-пустышку свойства возможно определить только внутри класса или структуры (то что имеет внутри себя this если говорить проще). На него можно получить указатель как на обычный член класса (это очень обязательное условие, иначе все идеи использования свойств совместно с рефлексией отпадают), только тип поля будет определятся по типу которое наследует свойство (чтобы при доставании типа данных через специализацию, которое храни свойство, все было хорошо). Внутри скопа также будет доступен this, который будет указателем на класс, внутри которого находится свойство. Теперь пример кода:

#include <iostream>

// Внешнее определение нового шаблонного свойства, которое устанавливает значение полю возводя его во вторую степень
template<auto Field>
property PPow2 : float // value может быть float
{
	set // скоп сеттера, работает только внутри скопа property
	{
		(this->*Field) = value * value; // this это Vehicle*, value содержит новое значение, действует только внутри set и set_const
	}

	get_const // скоп константного геттера, работает только внутри скопа property
	{
		return this->*Field; // this это const Vehicle*, const из-за get_const, в обычном get указатель будет не константный
	}
};

class Vehicle
{
private:
	float _Health = 0.f;

	void AddSeat()
	{
		std::cout << "NEW SEAT" << std::endl;
	}

	unsigned _MaxPassenger = 0;

public:
	PPow2<&Vehicle::_Health> Health;

	// Внутреннее определение свойства (без имени)
	property : unsigned
	{
		set
		{
			for (unsigned i = 0; i < value; ++i)
				AddSeat();

			_MaxPassenger = value;
		}

		get_const
		{
			return _MaxPassenger;
		}
	}
	MaxPassenger = 100;
};

constexpr unsigned Vehicle::* MaxPassenger = &Vehicle::MaxPassenger;

int main()
{
	Vehicle infernus;
	
	infernus.*MaxPassenger = 1337;
	infernus.Health = 100.f;
}

Думаю объяснил все максимально кратко и доступно. Если какой-то момент не понятен могу дополнить. Возможно уже кто-то предлагал данную идею, но я не нашел.

Класс для работы с плавающей запятой по стандарту ieee 754

Перенос предложения: голоса +0, -5
Автор идеи: Мартынов Иван @VanyaClassicTGN

Доброго времени суток, друзья.

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

Привожу на Ваш суд свой класс работы с плавающей точкой.
https://github.com/VanyaClassicTGN/Utils/blob/main/Float.h

Позволяет писать такой код:

float_t a = 10; float_t b = 0.3;

auto res = a + b;

или bool flag = a > b;

Дополнительно реализовано бинарное сравнение чисел с плавающей точкой.

С уважением, Мартынов Иван Евгеньевич

Функции (методы) расширения

Перенос предложения: голоса +5, -1
Aвтор идеи: mezastel

В текущей парадигме, чтобы добавить то или иное поведение классу, который нельзя менять, используются глобальные функции. Типичный пример - это строковые алгоритмы в Boost вроде boost::to_lower_copy(). Проблема с этими функциями - они все глобальны, их сложно искать, и цепочные вызовы таких функций вылгядят как f(g(x)) вместо x.g().f(). Хочется получать то же, что имеют разработчики C#, где методы расширения реализуются специальным ключевым словом.

Я предлагаю в С++ сделать то же самое вплоть до синтаксиса, то есть

namespace std
{
  static std::string to_lower_copy(this string s)
  {
    ...
  }
}

Ключевое слово this тут помечает функцию как функцию-расширение, так что ее можно использовать

  • Естественным путем, т.е. my_string.to_lower_copy()
  • Статическим путем, т.е. std::to_lower_copy(my_string)

Второй способ нужен в случае коллизий между member functions и extension functions.

Базовый тип byte

Перенос предложения: голоса +20, -1
Aвтор идеи: y.belykhav

В C++ нет встроенного типа byte . Для представления байтовых значений используется тип unsigned char.

Не красиво выглядет char - символ. Все делают свой byte|BYTE.

Making all instances of tag types inline

Перенос предложения: голоса +6, -1
Aвтор идеи: Anton Bikineev

Расммотрим 3.2.6.2 basic.def.odr:

in each definition of D, corresponding names, looked up accordingly to 3.4, shall refer to an entity defined within the definition of D, or shall refer to the same entity, after overload resolution and after matching of partial template specialization, except that a name can refer to

  • a non-volatile const object with internal or no linkage if the object
  • has the same literal type in all definitions of D,
  • is initialized with a constant expression,
  • is not odr-used, and
  • has the same value in all definitions of D.

Теперь рассмотрим, к примеру, пример определения экзекьюшн-политики и параллельного алгоритма в стандартной библиотеке:

constexpr sequential_execution_policy seq{};

template<class ExecutionPolicy, class InputIterator>
typename std::iterator_traits<InputIt>::value_type reduce(
    ExecutionPolicy&& policy, InputIt first, InputIt last);

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

Выдвигается идея сделать следующие объекты из стандартной библиотеки инлайновыми:

    std::placeholders::_1, ...
    piecewise_construct
    allocator_arg
    nullopt
    ignore
    defer_lock
    try_to_lock
    adopt_lock
    seq
    par
    par_unseq

Данная проблема также рассмотрена в National Body комментах (GB28, FI9).

Aggregate initialization для классов с виртуальными методами

Перенос предложения: голоса +3, -1
Автор идеи: waka.packmaca

Если класс наследуется от класса с виртуальными методами, aggregate initialization для него запрещена. Не вижу ни одной причине из-за которой такое кощунство возможно возможно. Хотелось бы, чтобы это ограничение убрали.

В связи с имеющимся ограничением следующий код невалиден:

struct Serializer {
  virtual ~Serializer() = default;
  virtual void write(std::int64_t) = 0;
};

struct Serializable {
  virtual ~Serializable() = default;
  virtual void serialize(Serializer &s) const = 0;
};

struct Object : Serializable {
  std::int64_t value;

  void serialize(Serializer &s) const override {
    s.write(value);
  }
};

// ...

Object {.value = 16};

Добавить возможность установки имени потока для std::thread

Перенос предложения: голоса +9, -0
Автор идеи: Arkady Shapkin

Этот вызов внутри может вызывать указанные ниже функции:
Под Windows SetThreadDescription https://docs.microsoft.com/ru-ru/windows/win32/api/processthreadsapi/nf-processthreadsapi-setthreaddescription?redirectedfrom=MSDN
Под Linux есть pthread_setname_np (https://linux.die.net/man/3/pthread_setname_np) (есть ограничение в 16 символов)

В других языках есть подобный функционал:

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.