1

Тема: Питання щодо merge sort

Знову завдання з LeetCode.
Намагаюся написати merge sort з рекурсією:

#include <iostream>
#include <vector>

void merge_array(std::vector<int> &arr, int &start, int &middle, int &end) {
    const int &first_half_size = middle - start + 1;
    const int &second_half_size = end - middle;
    std::vector<int> first_half;
    std::vector<int> second_half;

    for (int i = start; i < first_half_size; ++i) {
        first_half.push_back(arr.at(i));
    }
    for (int j = middle + 1; j < second_half_size; ++j) {
        second_half.push_back(arr.at(j));
    }

    int index_first = 0;
    int index_second = 0;
    arr.clear();
    while (index_first < first_half_size && index_second < second_half_size) {
        if (first_half.at(index_first) <= second_half.at(index_second)) {
            arr.push_back(first_half.at(index_first));
            ++index_first;
        } else {
            arr.push_back(second_half.at(index_second));
            ++index_second;
        }
    }

    /*if (index_first < first_half_size) {
        std::vector<int> sub_first(first_half.begin() + index_first, first_half.end());
        arr.insert(arr.end(), sub_first.begin(), sub_first.end());
    }
    if (index_second < second_half_size) {
        std::vector<int> sub_second(second_half.begin() + index_second, second_half.end());
        arr.insert(arr.end(), sub_second.begin(), sub_second.end());
    }*/
}

void divide_array(std::vector<int> &arr, int &start, int &end) {
    if (start < end) {
        int middle = start + (end - start) / 2;
        divide_array(arr, start, middle);
        divide_array(arr, ++middle, end);
        merge_array(arr, start, middle, end);
    }
}

std::vector<int> sortArray(std::vector<int> &nums) {
    int start { 0 };
    int end = nums.size();
    divide_array(nums, start, end);
    return nums;
}

int main() {
    std::vector<int> nums { 5, 2, 3, 1 };
    std::vector<int> sorted_array = sortArray(nums);
    return 0;
}

Видає помилку:

[ 50%] Building CXX object CMakeFiles/merge_sort.dir/main.cpp.o
[100%] Linking CXX executable merge_sort
terminate called after throwing an instance of 'std::out_of_range'
  what():  vector::_M_range_check: __n (which is 0) >= this->size() (which is 0)
make[2]: *** [CMakeFiles/merge_sort.dir/build.make:98: merge_sort] Перервано (зроблений дамп пам'яті)
make[2]: *** Вилучаємо файл "merge_sort"
make[1]: *** [CMakeFiles/Makefile2:83: CMakeFiles/merge_sort.dir/all] Помилка 2
make: *** [Makefile:136: all] Помилка 2

Розумію, що звертаюся за хибним індексом, якого не існує, але не розумію, де саме.
Чому компілятор хоча б рядка не назве, де помилка сталася?
Чи є якесь розширення для С++, яке виводитиме помилки більш деталізовано й зрозуміло для звичайної людини?

2

Re: Питання щодо merge sort

Тут, та в подiбних дiлянках:

for (int i = start; i < first_half_size; ++i) {
        first_half.push_back(arr.at(i));
    }

А якщо "i" буде менше нуля або бiльше (чи дорiвнювати) arr.size() ? Повиннi бути перевiрки.

Не по темi, але

void merge_array(std::vector<int> &arr, int &start, int &middle, int &end) {
    const int &first_half_size = middle - start + 1;
    const int &second_half_size = end - middle;

Чому б не

void merge_array(const std::vector<int> &arr, const int &start, const int &middle, const int &end) {
    const int first_half_size = middle - start + 1;
    const int second_half_size = end - middle;

Не вчитувався далi, але алгоритм якiйсь дивний.

Подякували: Teg Miles1

3

Re: Питання щодо merge sort

asdf написав:

Тут, та в подiбних дiлянках:

for (int i = start; i < first_half_size; ++i) {
        first_half.push_back(arr.at(i));
    }

А якщо "i" буде менше нуля або бiльше (чи дорiвнювати) arr.size() ? Повиннi бути перевiрки.

Не по темi, але

void merge_array(std::vector<int> &arr, int &start, int &middle, int &end) {
    const int &first_half_size = middle - start + 1;
    const int &second_half_size = end - middle;

Чому б не

void merge_array(const std::vector<int> &arr, const int &start, const int &middle, const int &end) {
    const int first_half_size = middle - start + 1;
    const int second_half_size = end - middle;

Не вчитувався далi, але алгоритм якiйсь дивний.

Тоді можна замінити на ось це:

    for (int i = 0; start + i < first_half_size; ++i) {
        first_half.push_back(arr.at(start + i));
    }
    for (int j = 0; middle + 1 + j < second_half_size; ++j) {
        second_half.push_back(arr.at(middle + 1 + j));
    }

4

Re: Питання щодо merge sort

Teg Miles написав:

Чому компілятор хоча б рядка не назве, де помилка сталася?

Для того код треба компільнути з додатковою інфо.
Якщо з компілером йде важко, то в Makefile [або в CMakeLists] додай шось таке:

set(CMAKE_BUILD_TYPE "Debug")
set(CMAKE_C_FLAGS_DEBUG "-g -O0 -DDEBUG" ${CMAKE_C_FLAGS_DEBUG})
set(CMAKE_CXX_FLAGS_DEBUG "-g -O0 -DDEBUG" ${CMAKE_CXX_FLAGS_DEBUG})

Потім компільни і в IDE виконай програму крок-за-крок.

Коли завершиш debug, то видали додані рядки.

Подякували: Teg Miles1

5 Востаннє редагувалося Teg Miles (04.08.2024 16:37:34)

Re: Питання щодо merge sort

leofun01 написав:
Teg Miles написав:

Чому компілятор хоча б рядка не назве, де помилка сталася?

Для того код треба компільнути з додатковою інфо.
Якщо з компілером йде важко, то в Makefile [або в CMakeLists] додай шось таке:

set(CMAKE_BUILD_TYPE "Debug")
set(CMAKE_C_FLAGS_DEBUG "-g -O0 -DDEBUG" ${CMAKE_C_FLAGS_DEBUG})
set(CMAKE_CXX_FLAGS_DEBUG "-g -O0 -DDEBUG" ${CMAKE_CXX_FLAGS_DEBUG})

Потім компільни і в IDE виконай програму крок-за-крок.

Коли завершиш debug, то видали додані рядки.

У налаштуваннях Vim програма компілюється ось так:

" Збирання проєкту С++ за допомогою makeprg версія Debug клавішею <F6>
nmap <F6> :!cmake -Bbuild/Debug -DCMAKE_BUILD_TYPE=Debug && cd build/Debug && make<CR>
imap <F6> <Esc>:!cmake -Bbuild/Debug -DCMAKE_BUILD_TYPE=Debug && cd build/Debug && make<CR>i

Цього замало, щоб увімкнути Debug для детального виводу помилок?

Чи треба додати -g до нижченаведеної змінної opts?

" Встановлення прапорців для gcc/clang
let opts = '-std=c++17 -Wall -Wextra -Werror'
let g:ale_cpp_cc_options    = opts
let g:ale_cpp_gcc_options   = opts
let g:ale_cpp_clang_options = opts
let g:ale_c_build_dir_names = ['build', 'bin', 'build/Debug', 'build/Release'] "Директорії, де ALE шукає compile_commands.json

6

Re: Питання щодо merge sort

Teg Miles написав:

У налаштуваннях Vim програма компілюється ось так:

" Збирання проєкту С++ за допомогою makeprg версія Debug клавішею <F6>
nmap <F6> :!cmake -Bbuild/Debug -DCMAKE_BUILD_TYPE=Debug && cd build/Debug && make<CR>
imap <F6> <Esc>:!cmake -Bbuild/Debug -DCMAKE_BUILD_TYPE=Debug && cd build/Debug && make<CR>i

Цього замало, щоб увімкнути Debug для детального виводу помилок?

Чи треба додати -g до нижченаведеної змінної opts?

" Встановлення прапорців для gcc/clang
let opts = '-std=c++17 -Wall -Wextra -Werror'
let g:ale_cpp_cc_options    = opts
let g:ale_cpp_gcc_options   = opts
let g:ale_cpp_clang_options = opts
let g:ale_c_build_dir_names = ['build', 'bin', 'build/Debug', 'build/Release'] "Директорії, де ALE шукає compile_commands.json

На всі ці питаня відповідь: не знаю. По ідеї, сам CMake додає -g коли бачить CMAKE_BUILD_TYPE=Debug. Але переписуючи конфіґи його легко поламати.
Пробуй додати -g в opts, і потім не забуть видалити.

Після прочитаного, я гадаю, проблема не в тому що якоїсь інфо не вистачає в бінарнику, гадаю ти покищо не знаєш як робити debug через gdb. Залишу це тут: команди GDB.

Подякували: Teg Miles1

7

Re: Питання щодо merge sort

Помилка була в неправильному підході до ділення масиву.
Довелося повністю переробляти, доки не знайшов робочу формулу.
Ось таке рішення прийняв LeetCode:

void merge_array(std::vector<int> &main_array, std::vector<int> &first_half,
    std::vector<int> &second_half) {
    int index_first { 0 };
    int index_second { 0 };
    int index_merged_arr = { 0 };

    while (index_first < first_half.size() && index_second < second_half.size()) {
        if (first_half.at(index_first) < second_half.at(index_second)) {
            main_array.at(index_merged_arr) = first_half.at(index_first);
            ++index_first;
        } else {
            main_array.at(index_merged_arr) = second_half.at(index_second);
            ++index_second;
        }
        ++index_merged_arr;
    }
    while (index_first < first_half.size()) {
        main_array.at(index_merged_arr) = first_half.at(index_first);
        ++index_first;
        ++index_merged_arr;
    }
    while (index_second < second_half.size()) {
        main_array.at(index_merged_arr) = second_half.at(index_second);
        ++index_second;
        ++index_merged_arr;
    }
}

void divide_array(std::vector<int> &arr) {
    if (arr.size() > 1) {
        std::vector<int> first_half(arr.begin(), arr.begin() + arr.size() / 2);
        std::vector<int> second_half(arr.begin() + arr.size() / 2, arr.begin() + arr.size());
        divide_array(first_half);
        divide_array(second_half);
        merge_array(arr, first_half, second_half);
    }
}

std::vector<int> sortArray(std::vector<int> &nums) {
    if (nums.size() > 1) {
        divide_array(nums);
    }
    return nums;
}

До речі, якщо працюєте з контейнерами і треба дістати щось за індексом,
то краще використовувати оператор .at(), а не оператор [].
Бо .at() при неправильному індексі видає вищезгадану помилку,
а [] просто тихенько видає вам нулик (якщо масив був з цілими числами).

8 Востаннє редагувалося steamwater (05.08.2024 13:17:48)

Re: Питання щодо merge sort

До речі, якщо працюєте з контейнерами і треба дістати щось за індексом,
то краще використовувати оператор .at(), а не оператор [].
Бо .at() при неправильному індексі видає вищезгадану помилку,
а [] просто тихенько видає вам нулик (якщо масив був з цілими числами).

Нi. Так роблять дуже рiдко, бо перевiрка дiапазону додаватиме двi перевiрки, на валiднiсть адреси. Треба самому гарантувати валiднiсть адресу у створеному алгоритмi. Метод at для ледащiх, у бiльшостi випадкiв i часто є темою хлiварiв. Тож моя порада, не кажiть такого десь на спiвбесидi. Мiнус буде не великий, але буде.

9

Re: Питання щодо merge sort

steamwater написав:

До речі, якщо працюєте з контейнерами і треба дістати щось за індексом,
то краще використовувати оператор .at(), а не оператор [].
Бо .at() при неправильному індексі видає вищезгадану помилку,
а [] просто тихенько видає вам нулик (якщо масив був з цілими числами).

Нi. Так роблять дуже рiдко, бо перевiрка дiапазону додаватиме двi перевiрки, на валiднiсть адреси. Треба самому гарантувати валiднiсть адресу у створеному алгоритмi. Метод at для ледащiх, у бiльшостi випадкiв i часто є темою хлiварiв. Тож моя порада, не кажiть такого десь на спiвбесидi. Мiнус буде не великий, але буде.

У мене так було, коли робив merge sort.
При завеликих індексах оператор [] просто нулі видавав, а не помилку.
Я їх бачив у відсортованому масиві, хоча в оригіналі нулів не було.

10

Re: Питання щодо merge sort

Teg Miles написав:

До речі, якщо працюєте з контейнерами і треба дістати щось за індексом,
то краще використовувати оператор .at(), а не оператор [].
Бо .at() при неправильному індексі видає вищезгадану помилку,
а [] просто тихенько видає вам нулик (якщо масив був з цілими числами).

Вихід за межі масиву – UB, загалом, в такому випадку компілятор не зобов'язаний надавати жодних діагностик. Але це не означає, що його не можна про це попросити.
Повторюсь ще раз, такий підхід (методом тику), далеко вас не заведе. Ні, звісно можна пробувати випадкові речі й сподіватися, що компілятор це пропустить/програма не впаде. Але ви ж так і не зрозумієте, чому так сталося і що можна зробити. Приклад:

#include <vector>
int main() {
    std::vector<int> v;
    return v[100];
}

> compile

g++ -o main main.cpp -std=c++20 -Wall -Wextra -Wpedantic -fsanitize=address,undefined -D_GLIBCXX_DEBUG -g -O0

> out

Прихований текст

Program returned: 1
Program stderr
/opt/gcc-14.1.0/include/c++/14.1.0/debug/vector:508:
In function:
    constexpr std::debug::vector<_Tp, _Allocator>::reference std::
    debug::vector<_Tp, _Allocator>::operator[](size_type) [with _Tp = int;
    _Allocator = std::allocator<int>; reference = int&; size_type = long
    unsigned int]

Error: attempt to subscript container with out-of-bounds index 100, but
container only holds 0 elements.

Objects involved in the operation:
    sequence "this" @ 0x7bab53100020 {
      type = std::debug::vector<int, std::allocator<int> >;
    }
AddressSanitizer:DEADLYSIGNAL
=================================================================
==1==ERROR: AddressSanitizer: SEGV on unknown address (pc 0x7bab54e28898 bp 0x7bab5501be90 sp 0x7ffd1b2f5b00 T0)
==1==The signal is caused by a READ memory access.
==1==Hint: this fault was caused by a dereference of a high value address (see register values below).  Disassemble the provided pc to learn which register was used.
    #0 0x7bab54e28898 in abort (/lib/x86_64-linux-gnu/libc.so.6+0x28898) (BuildId: 490fef8403240c91833978d494d39e537409b92e)
    #1 0x7bab5584d1c2  (/opt/gcc-14.1.0/lib64/libstdc++.so.6+0xb01c2) (BuildId: a436cc09b39e5efb87974aa327eeae4272fd1b23)
    #2 0x403a4e in std::__debug::vector<int, std::allocator<int> >::operator[](unsigned long) /opt/compiler-explorer/gcc-14.1.0/include/c++/14.1.0/debug/vector:508
    #3 0x4023f8 in main main.cpp:4
    #4 0x7bab54e29d8f  (/lib/x86_64-linux-gnu/libc.so.6+0x29d8f) (BuildId: 490fef8403240c91833978d494d39e537409b92e)
    #5 0x7bab54e29e3f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x29e3f) (BuildId: 490fef8403240c91833978d494d39e537409b92e)
    #6 0x4021e4 in _start (main.s+0x4021e4) (BuildId: 3202272c5c10ff79ea754c5b4f416e4a27b534ed)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV (/lib/x86_64-linux-gnu/libc.so.6+0x28898) (BuildId: 490fef8403240c91833978d494d39e537409b92e) in abort
==1==ABORTING

Посилання на godbolt.

Подякували: Teg Miles, leofun012

11 Востаннє редагувалося Teg Miles (05.08.2024 17:58:11)

Re: Питання щодо merge sort

wander написав:
Teg Miles написав:

До речі, якщо працюєте з контейнерами і треба дістати щось за індексом,
то краще використовувати оператор .at(), а не оператор [].
Бо .at() при неправильному індексі видає вищезгадану помилку,
а [] просто тихенько видає вам нулик (якщо масив був з цілими числами).

Вихід за межі масиву – UB, загалом, в такому випадку компілятор не зобов'язаний надавати жодних діагностик. Але це не означає, що його не можна про це попросити.
Повторюсь ще раз, такий підхід (методом тику), далеко вас не заведе. Ні, звісно можна пробувати випадкові речі й сподіватися, що компілятор це пропустить/програма не впаде. Але ви ж так і не зрозумієте, чому так сталося і що можна зробити. Приклад:

#include <vector>
int main() {
    std::vector<int> v;
    return v[100];
}

> compile

g++ -o main main.cpp -std=c++20 -Wall -Wextra -Wpedantic -fsanitize=address,undefined -D_GLIBCXX_DEBUG -g -O0

> out

Прихований текст

Program returned: 1
Program stderr
/opt/gcc-14.1.0/include/c++/14.1.0/debug/vector:508:
In function:
    constexpr std::debug::vector<_Tp, _Allocator>::reference std::
    debug::vector<_Tp, _Allocator>::operator[](size_type) [with _Tp = int;
    _Allocator = std::allocator<int>; reference = int&; size_type = long
    unsigned int]

Error: attempt to subscript container with out-of-bounds index 100, but
container only holds 0 elements.

Objects involved in the operation:
    sequence "this" @ 0x7bab53100020 {
      type = std::debug::vector<int, std::allocator<int> >;
    }
AddressSanitizer:DEADLYSIGNAL
=================================================================
==1==ERROR: AddressSanitizer: SEGV on unknown address (pc 0x7bab54e28898 bp 0x7bab5501be90 sp 0x7ffd1b2f5b00 T0)
==1==The signal is caused by a READ memory access.
==1==Hint: this fault was caused by a dereference of a high value address (see register values below).  Disassemble the provided pc to learn which register was used.
    #0 0x7bab54e28898 in abort (/lib/x86_64-linux-gnu/libc.so.6+0x28898) (BuildId: 490fef8403240c91833978d494d39e537409b92e)
    #1 0x7bab5584d1c2  (/opt/gcc-14.1.0/lib64/libstdc++.so.6+0xb01c2) (BuildId: a436cc09b39e5efb87974aa327eeae4272fd1b23)
    #2 0x403a4e in std::__debug::vector<int, std::allocator<int> >::operator[](unsigned long) /opt/compiler-explorer/gcc-14.1.0/include/c++/14.1.0/debug/vector:508
    #3 0x4023f8 in main main.cpp:4
    #4 0x7bab54e29d8f  (/lib/x86_64-linux-gnu/libc.so.6+0x29d8f) (BuildId: 490fef8403240c91833978d494d39e537409b92e)
    #5 0x7bab54e29e3f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x29e3f) (BuildId: 490fef8403240c91833978d494d39e537409b92e)
    #6 0x4021e4 in _start (main.s+0x4021e4) (BuildId: 3202272c5c10ff79ea754c5b4f416e4a27b534ed)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV (/lib/x86_64-linux-gnu/libc.so.6+0x28898) (BuildId: 490fef8403240c91833978d494d39e537409b92e) in abort
==1==ABORTING

Посилання на godbolt.

Я додав до налаштувань vimrc рядок -std=c++20 -Wall -Wextra -Wpedantic -fsanitize=address,undefined -D_GLIBCXX_DEBUG -g -O0,
але вивід усе одно ось такий(використав ваш приклад):

make[2]: *** [CMakeFiles/hello_world.dir/build.make:98: hello_world] Помилка адресування (зроблений дамп пам'яті)
make[2]: *** Вилучаємо файл "hello_world"
make[1]: *** [CMakeFiles/Makefile2:83: CMakeFiles/hello_world.dir/all] Помилка 2
make: *** [Makefile:136: all] Помилка 2

Налаштування Vim:

" Налаштування ALE С++
" Прив'язка файлів .h для C++, а не C
let g:ale_pattern_options_enabled = 1
let g:ale_pattern_options = { '\.h$': { 'ale_linters': { 'cpp' : ['cc', 'gcc', 'clang', 'clangd'] } } }
" Встановлення прапорців для gcc/clang
let opts = '-std=c++20 -Wall -Wextra -Wpedantic -fsanitize=address,undefined -D_GLIBCXX_DEBUG -g -O0'
let g:ale_cpp_cc_options    = opts
let g:ale_cpp_gcc_options   = opts
let g:ale_cpp_clang_options = opts
let g:ale_c_build_dir_names = ['build', 'bin', 'build/Debug', 'build/Release'] "Директорії, де ALE шукає compile_commands.json

" Збирання проєкту С++ за допомогою makeprg версія Debug клавішею <F6>
nmap <F6> :!cmake -Bbuild/Debug -DCMAKE_BUILD_TYPE=Debug && cd build/Debug && make<CR>
imap <F6> <Esc>:!cmake -Bbuild/Debug -DCMAKE_BUILD_TYPE=Debug && cd build/Debug && make<CR>i

Як правильно налаштувати Vim, щоб вивід був деталізований, як у вас?

12

Re: Питання щодо merge sort

З Vim – хз, я використовую neovim з плагіном для cmake (для майже всіх випадків).
Даний уривок я зібрав напряму з командного рядка ручками, як виключення, бо так було швидше.
Можу підказати з налаштуванням CMakeLists.txt, а як той vim у вас збирає я не знаю.

Подякували: leofun011

13

Re: Питання щодо merge sort

wander написав:

З Vim – хз, я використовую neovim з плагіном для cmake (для майже всіх випадків).
Даний уривок я зібрав напряму з командного рядка ручками, як виключення, бо так було швидше.
Можу підказати з налаштуванням CMakeLists.txt, а як той vim у вас збирає я не знаю.

Підкажіть із CMakeLists.txt, я всі проєкти з ним роблю.

14

Re: Питання щодо merge sort

Ось так треба зробити в CMakeLists, щоб додати прапори до потрібної версії (Debug, Release чи до всіх одночасно):

set(CMAKE_CXX_FLAGS_DEBUG "-std=c++20 -Wall -Wextra -Wpedantic -fsanitize=address,undefined -D_GLIBCXX_DEBUG -g -O0")
#set(CMAKE_CXX_FLAGS_RELEASE "")
#set(CMAKE_CXX_FLAGS "")

Два останніх закоментив, бо ще не знаю, яку там конфігурацію краще обрати.

15

Re: Питання щодо merge sort

Teg Miles написав:
wander написав:

З Vim – хз, я використовую neovim з плагіном для cmake (для майже всіх випадків).
Даний уривок я зібрав напряму з командного рядка ручками, як виключення, бо так було швидше.
Можу підказати з налаштуванням CMakeLists.txt, а як той vim у вас збирає я не знаю.

Підкажіть із CMakeLists.txt, я всі проєкти з ним роблю.

Ну, можна якось так:

Прихований текст
cmake_minimum_required(VERSION 3.20)

project(test)

include(CheckCXXSymbolExists)
check_cxx_symbol_exists(_LIBCPP_VERSION "cstddef" IS_LIBCXX)

set(CXX_LIBSTD_DEBUG_FLAGS)
if(${CMAKE_BUILD_TYPE} STREQUAL "Debug")
    if(IS_LIBCXX)
        # NOTE: debug mode is immature in Clang, and values of _LIBCPP_DEBUG above 0
        # require  the debug build of libc++ to be present at linktime.
        list(APPEND CXX_LIBSTD_DEBUG_FLAGS _LIBCPP_DEBUG=1)
    else()
        # Enable diagnostic features of standard class templates, including ability
        # to examine containers in gdb.
        # See https://gcc.gnu.org/onlinedocs/libstdc++/manual/debug_mode_using.html
        list(APPEND CXX_LIBSTD_DEBUG_FLAGS _GLIBCXX_DEBUG)
    endif()
endif()

option(CXX_ENABLE_SANITIZER_ADDRESS   "Enable address sanitizer." YES)
option(CXX_ENABLE_SANITIZER_UNDEFINED "Enable UB sanitizer."      YES)

set(CXX_SANITIZERS "")

if(CXX_ENABLE_SANITIZER_ADDRESS)
    list(APPEND CXX_SANITIZERS "address")
endif()

if(CXX_ENABLE_SANITIZER_UNDEFINED)
    list(APPEND CXX_SANITIZERS "undefined")
endif()

list(JOIN CXX_SANITIZERS "," CXX_ENABLED_SANITIZERS)

add_executable(out_of_bounds_test
    main.cpp)
# Set c++20
target_compile_features(out_of_bounds_test PRIVATE cxx_std_20)
# Set c++ standard library debug flags
target_compile_definitions(out_of_bounds_test
    PRIVATE ${CXX_LIBSTD_DEBUG_FLAGS})
# Set sanitizers (if any)
target_compile_options(out_of_bounds_test
    PUBLIC -fsanitize=${CXX_ENABLED_SANITIZERS})
target_link_options(out_of_bounds_test
    PUBLIC -fsanitize=${CXX_ENABLED_SANITIZERS})

Посилання на godbolt.

Teg Miles написав:

Два останніх закоментив, бо ще не знаю, яку там конфігурацію краще обрати.

Перевизначати будь-які CMAKE_<LANG>_FLAGS_<BUILD_TYPE> – погана ідея (виняток: якщо інакше не обійтись).

Подякували: leofun011

16

Re: Питання щодо merge sort

wander написав:
Teg Miles написав:
wander написав:

З Vim – хз, я використовую neovim з плагіном для cmake (для майже всіх випадків).
Даний уривок я зібрав напряму з командного рядка ручками, як виключення, бо так було швидше.
Можу підказати з налаштуванням CMakeLists.txt, а як той vim у вас збирає я не знаю.

Підкажіть із CMakeLists.txt, я всі проєкти з ним роблю.

Ну, можна якось так:

Прихований текст
cmake_minimum_required(VERSION 3.20)

project(test)

include(CheckCXXSymbolExists)
check_cxx_symbol_exists(_LIBCPP_VERSION "cstddef" IS_LIBCXX)

set(CXX_LIBSTD_DEBUG_FLAGS)
if(${CMAKE_BUILD_TYPE} STREQUAL "Debug")
    if(IS_LIBCXX)
        # NOTE: debug mode is immature in Clang, and values of _LIBCPP_DEBUG above 0
        # require  the debug build of libc++ to be present at linktime.
        list(APPEND CXX_LIBSTD_DEBUG_FLAGS _LIBCPP_DEBUG=1)
    else()
        # Enable diagnostic features of standard class templates, including ability
        # to examine containers in gdb.
        # See https://gcc.gnu.org/onlinedocs/libstdc++/manual/debug_mode_using.html
        list(APPEND CXX_LIBSTD_DEBUG_FLAGS _GLIBCXX_DEBUG)
    endif()
endif()

option(CXX_ENABLE_SANITIZER_ADDRESS   "Enable address sanitizer." YES)
option(CXX_ENABLE_SANITIZER_UNDEFINED "Enable UB sanitizer."      YES)

set(CXX_SANITIZERS "")

if(CXX_ENABLE_SANITIZER_ADDRESS)
    list(APPEND CXX_SANITIZERS "address")
endif()

if(CXX_ENABLE_SANITIZER_UNDEFINED)
    list(APPEND CXX_SANITIZERS "undefined")
endif()

list(JOIN CXX_SANITIZERS "," CXX_ENABLED_SANITIZERS)

add_executable(out_of_bounds_test
    main.cpp)
# Set c++20
target_compile_features(out_of_bounds_test PRIVATE cxx_std_20)
# Set c++ standard library build flags
target_compile_definitions(out_of_bounds_test
    PRIVATE ${CXX_LIBSTD_DEBUG_FLAGS})
# Set sanitizers (if any)
target_compile_options(out_of_bounds_test
    PUBLIC -fsanitize=${CXX_ENABLED_SANITIZERS})
target_link_options(out_of_bounds_test
    PUBLIC -fsanitize=${CXX_ENABLED_SANITIZERS})

Посилання на godbolt.

Teg Miles написав:

Два останніх закоментив, бо ще не знаю, яку там конфігурацію краще обрати.

Перевизначати будь-які CMAKE_<LANG>_FLAGS_<BUILD_TYPE> – погана ідея (виняток: якщо інакше не обійтись).

А якщо ось так:

add_compile_options(
"$<$<COMPILE_LANGUAGE:CXX>:-Ofast;-DNDEBUG;-std=c++20;-march=native;-fpic;-ftree-vectorize>"
)

17

Re: Питання щодо merge sort

Teg Miles написав:
wander написав:
Teg Miles написав:

Підкажіть із CMakeLists.txt, я всі проєкти з ним роблю.

Ну, можна якось так:

Прихований текст
cmake_minimum_required(VERSION 3.20)

project(test)

include(CheckCXXSymbolExists)
check_cxx_symbol_exists(_LIBCPP_VERSION "cstddef" IS_LIBCXX)

set(CXX_LIBSTD_DEBUG_FLAGS)
if(${CMAKE_BUILD_TYPE} STREQUAL "Debug")
    if(IS_LIBCXX)
        # NOTE: debug mode is immature in Clang, and values of _LIBCPP_DEBUG above 0
        # require  the debug build of libc++ to be present at linktime.
        list(APPEND CXX_LIBSTD_DEBUG_FLAGS _LIBCPP_DEBUG=1)
    else()
        # Enable diagnostic features of standard class templates, including ability
        # to examine containers in gdb.
        # See https://gcc.gnu.org/onlinedocs/libstdc++/manual/debug_mode_using.html
        list(APPEND CXX_LIBSTD_DEBUG_FLAGS _GLIBCXX_DEBUG)
    endif()
endif()

option(CXX_ENABLE_SANITIZER_ADDRESS   "Enable address sanitizer." YES)
option(CXX_ENABLE_SANITIZER_UNDEFINED "Enable UB sanitizer."      YES)

set(CXX_SANITIZERS "")

if(CXX_ENABLE_SANITIZER_ADDRESS)
    list(APPEND CXX_SANITIZERS "address")
endif()

if(CXX_ENABLE_SANITIZER_UNDEFINED)
    list(APPEND CXX_SANITIZERS "undefined")
endif()

list(JOIN CXX_SANITIZERS "," CXX_ENABLED_SANITIZERS)

add_executable(out_of_bounds_test
    main.cpp)
# Set c++20
target_compile_features(out_of_bounds_test PRIVATE cxx_std_20)
# Set c++ standard library build flags
target_compile_definitions(out_of_bounds_test
    PRIVATE ${CXX_LIBSTD_DEBUG_FLAGS})
# Set sanitizers (if any)
target_compile_options(out_of_bounds_test
    PUBLIC -fsanitize=${CXX_ENABLED_SANITIZERS})
target_link_options(out_of_bounds_test
    PUBLIC -fsanitize=${CXX_ENABLED_SANITIZERS})

Посилання на godbolt.

Teg Miles написав:

Два останніх закоментив, бо ще не знаю, яку там конфігурацію краще обрати.

Перевизначати будь-які CMAKE_<LANG>_FLAGS_<BUILD_TYPE> – погана ідея (виняток: якщо інакше не обійтись).

А якщо ось так:

add_compile_options(
"$<$<COMPILE_LANGUAGE:CXX>:-Ofast;-DNDEBUG;-std=c++20;-march=native;-fpic;-ftree-vectorize>"
)

Так тут ж зовсім інші опції виставлені.. Вам обов'язково в один рядок?

18

Re: Питання щодо merge sort

wander написав:
Teg Miles написав:
wander написав:

Ну, можна якось так:

Прихований текст
cmake_minimum_required(VERSION 3.20)

project(test)

include(CheckCXXSymbolExists)
check_cxx_symbol_exists(_LIBCPP_VERSION "cstddef" IS_LIBCXX)

set(CXX_LIBSTD_DEBUG_FLAGS)
if(${CMAKE_BUILD_TYPE} STREQUAL "Debug")
    if(IS_LIBCXX)
        # NOTE: debug mode is immature in Clang, and values of _LIBCPP_DEBUG above 0
        # require  the debug build of libc++ to be present at linktime.
        list(APPEND CXX_LIBSTD_DEBUG_FLAGS _LIBCPP_DEBUG=1)
    else()
        # Enable diagnostic features of standard class templates, including ability
        # to examine containers in gdb.
        # See https://gcc.gnu.org/onlinedocs/libstdc++/manual/debug_mode_using.html
        list(APPEND CXX_LIBSTD_DEBUG_FLAGS _GLIBCXX_DEBUG)
    endif()
endif()

option(CXX_ENABLE_SANITIZER_ADDRESS   "Enable address sanitizer." YES)
option(CXX_ENABLE_SANITIZER_UNDEFINED "Enable UB sanitizer."      YES)

set(CXX_SANITIZERS "")

if(CXX_ENABLE_SANITIZER_ADDRESS)
    list(APPEND CXX_SANITIZERS "address")
endif()

if(CXX_ENABLE_SANITIZER_UNDEFINED)
    list(APPEND CXX_SANITIZERS "undefined")
endif()

list(JOIN CXX_SANITIZERS "," CXX_ENABLED_SANITIZERS)

add_executable(out_of_bounds_test
    main.cpp)
# Set c++20
target_compile_features(out_of_bounds_test PRIVATE cxx_std_20)
# Set c++ standard library build flags
target_compile_definitions(out_of_bounds_test
    PRIVATE ${CXX_LIBSTD_DEBUG_FLAGS})
# Set sanitizers (if any)
target_compile_options(out_of_bounds_test
    PUBLIC -fsanitize=${CXX_ENABLED_SANITIZERS})
target_link_options(out_of_bounds_test
    PUBLIC -fsanitize=${CXX_ENABLED_SANITIZERS})

Посилання на godbolt.

Перевизначати будь-які CMAKE_<LANG>_FLAGS_<BUILD_TYPE> – погана ідея (виняток: якщо інакше не обійтись).

А якщо ось так:

add_compile_options(
"$<$<COMPILE_LANGUAGE:CXX>:-Ofast;-DNDEBUG;-std=c++20;-march=native;-fpic;-ftree-vectorize>"
)

Так тут ж зовсім інші опції виставлені.. Вам обов'язково в один рядок?

Опції я зміню на ті, що мені потрібні. Мене цікавить саме використання функції add_compile_options
і чи краще це ніж використання CMAKE_<LANG>_FLAGS_<BUILD_TYPE>.
Бажано один рядок, бо мені треба просто швидко налаштовувати середовище
відповідно до моїх потреб. А те, що ви пропонуєте занадто складно для мене,
принаймні зараз.

19

Re: Питання щодо merge sort

Ну, то візьміть лише останні рядки:

# Цією командою можна зручно встановити бажаний стандарт [c++17, c++20, c++23]
target_compile_features(<YOUR_TARGET> PUBLIC cxx_std_20)
# Цією командою можна легко додати дефайни, можна вписувати їх на місці
target_compile_definitions(<YOUR_TARGET> PUBLIC _GLIBCXX_DEBUG)
# Цією командою виставляємо прапорці компілятору
target_compile_options(<YOUR_TARGET>
    PUBLIC -Wall
           -Wextra
           -Wpedantic
           -fsanitize=address,undefined)
# Цією командою виставляємо прапорці лінкеру
target_link_options(<YOUR_TARGET>
    PUBLIC -fsanitize=address,undefined)

Санітайзер потрібно передавати і компілятору і лінкеру. Зможете з 4 командами впоратись?

Подякували: leofun011

20

Re: Питання щодо merge sort

wander написав:

Ну, то візьміть лише останні рядки:

# Цією командою можна зручно встановити бажаний стандарт [c++17, c++20, c++23]
target_compile_features(<YOUR_TARGET> PUBLIC cxx_std_20)
# Цією командою можна легко додати дефайни, можна вписувати їх на місці
target_compile_definitions(<YOUR_TARGET> PUBLIC _GLIBCXX_DEBUG)
# Цією командою виставляємо прапорці компілятору
target_compile_options(<YOUR_TARGET>
    PUBLIC -Wall
           -Wextra
           -Wpedantic
           -fsanitize=address,undefined)
# Цією командою виставляємо прапорці лінкеру
target_link_options(<YOUR_TARGET>
    PUBLIC -fsanitize=address,undefined)

Санітайзер потрібно передавати і компілятору і лінкеру. Зможете з 4 командами впоратись?

Додав:

# Цією командою можна зручно встановити бажаний стандарт [c++17, c++20, c++23]
target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_20)
# Цією командою можна легко додати дефайни, можна вписувати їх на місці
target_compile_definitions(${PROJECT_NAME} PUBLIC _GLIBCXX_DEBUG)
# Цією командою виставляємо прапорці компілятору
target_compile_options(${PROJECT_NAME}
    PUBLIC -Wall
           -Wextra
           -Wpedantic
           -fsanitize=address,undefined)
# Цією командою виставляємо прапорці лінкеру
target_link_options(${PROJECT_NAME}
    PUBLIC -fsanitize=address,undefined)

Отримав помилку:

CMake Error at CMakeLists.txt:16 (target_compile_features):
  Cannot specify compile features for target "hello_world" which is not built
  by this project.

CMake Error at CMakeLists.txt:18 (target_compile_definitions):
  Cannot specify compile definitions for target "hello_world" which is not
  built by this project.

CMake Error at CMakeLists.txt:20 (target_compile_options):
  Cannot specify compile options for target "hello_world" which is not built
  by this project.

CMake Error at CMakeLists.txt:26 (target_link_options):
  Cannot specify link options for target "hello_world" which is not built by
  this project.