__getCount
и после сборки проекта ты, внезапно, увидишь функцию в таблице экспорта.__attribute__((used))
[?] или [[gnu::used]]
на новый лад.#pragma comment(linker: "/include:")
[?].-u
[?] компоновщика./INCLUDE
[?].template <typename T>
T maxn(T arr[], int arrSize);
template <>
const char* maxn(const char* arr[], int arrSize);
char*
или const char*
если строку не планируется модифицировать. Наша функция не планирует модифицировать строки, следовательно явная специализация шаблона должна работать с типом const char*
.const char*
должен быть подставлен везде вместо параметра шаблона T
.template <> char* maxn(const char *arr[], int arrSize);
char*
отличен от параметра функции const char*
.template <>
const char* maxn<const char*>(const char* arr[], int arrSize);
inline std::vector<std::string_view> SplitString( std::string_view input_string, const char separator )
{
std::vector<std::string_view> parts;
size_t part_length = 0;
while( ( part_length = input_string.find( separator ) ) != input_string.npos )
{
parts.emplace_back( input_string.data(), part_length );
input_string.remove_prefix( part_length + 1 );
}
if( !input_string.empty() )
{
parts.push_back( std::move( input_string ) );
}
return parts;
}
class rck{
public:
unsigned short x,y;
void show();
int tm=0;
uint8_t type=0;
};
struct rck final
{
uint16_t x = 0;
uint16_t y = 0;
int32_t tm = 0;
uint8_t type = 0;
void show();
};
tm
и type
.rock.push_back({(uint16_t)(x*64),(uint16_t)(y*64),0,0});
push_back
формируется фиктивный std::initializer_list<uint16_t>
с двумя элементами внутри.rck
.tm
и type
). Каждый следующий стандарт требования к агрегатной инициализации только ужесточает.rck
не определено и тип не является агрегатным.LoadLibrary
и #include
, а в выборе между явным и неявным связыванием.LoadLibrary
[?] осуществляется в соответствии с определенным порядком. При этом, путями для поиска динамических библиотек можно управлять в ручном режиме, добавляя новые или замещая имеющиеся пути поиска.#include
[?] лишь косвенно относится к вопросу. На самом деле к вопросу относится директива #pragma comment( lib )
[?].#pragma comment( lib )
с такой статической библиотекой будет неявное связывание твоего кода с указанной динамической библиотекой.p = { s };
- это Copy Initialization.Protocol::Protocol(Serial &s)
. Следом, через operator =
, который сводится к конструктору копирования/перемещения, ты делаешь Copy Construction объекта p
. Делается это через конструктор копирования/перемещения по умолчанию, который просто копирует/перемещает поля используя их конструкторы копирования/перемещения.Serial
, а сам temporary local уничтожается сразу же после конструирования p
.this
.p = { s };
, а p{ s };
или, как ты уже описал, p = s;
. Такой тип инициализации уже называется Value Initialization. Но по своей сути это только отсрочка проблемы. Тебе придется очень пристально следить за жизнью такого объекта, т.к. вероятность обрести все тот же висячий указатель в замыкании в следствии неявного копирования крайне высока.std::enable_shared_from_this
[?], std::shared_ptr
[?] для хранения протокола и std::weak_ptr
[?] в замыкании твоей лямбды. Этот метод гарантированно решит твою проблему, но может привести к ряду других проблем с возможной утечкой ресурсов.Serial
отцеплять лямбду уничтожаемого Protocol
и написать правильный конструктор копирования Protocol
, соблюдая правило 3/5/0.[&p, &all_files_and_sums]
all_files_and_sums
по ссылке я не спорю, то точно ли p
продолжит существовать после выхода из итерации? Давай подумаем. А после выхода из цикла продолжит?bfs::directory_iterator
адрес возвращаемого им bfs::directory_entry
изменится. Я бы предпочел захватывать копию bfs::directory_entry
в лямбде.std::future::get
[?]. По факту в этот момент у тебя блокируется главный поток до момента обработки запланированной задачи. У тебя всегда планируется только одна задача. А видимость конкурентной работы создается лишь потому что какой поток ее схватил, тот и работает. Видимо задачи у тебя быстро обрабатываются, раз ты глазами этого не увидел.std::future
от задач. В момент планирования их результат еще не определен и get
вызывать не надо. Надо дождаться завершения работы всех потоков в пуле и исчерпания всех задач. Для этого у тебя в пуле должны быть продуманы механизмы оповещения.std::future::get
, получать результаты и производить свои операции над ними.std::future
результата. Это тоже можно сделать. Просто сделать это надо своими руками и продумав масштабируемость такого механизма.std::future
и лямбдами, ты пишешь в рамках стандарта C++11. Тебе доступны и std::thread
, и все примитивы барьерирования из std
.boost::filesystem
не будет нужна, т.к. станет доступна std::filesystem
- Filesystem Library. sizeof( expresion )
позволяет узнать минимально допустимый размер памяти, требуемый для того чтобы память могла вместить объект с типом результата утверждения expresion
.sizeof( value )
вернет размер LPCWSTR
, т.е. размер типа const wchar_t*
.wcslen
не допускает передачу нуля в качестве своего параметра. Забота об этом лежит полностью на тебе.RegSetValueExW
может принимать нуль в параметре lpData
, но тогда cbData
обязан содержать 0
. Но в целом, это очень плохая практика. Если тебе нужно записать пустую строку, пиши пустую. строку. Т.е. ""
, но не нуль.cbData
должен быть задан размером буфера строки в байтах, включая терминальный символ, или оба терминальных символа в случае многострочного аргумента в lpData
. Важным замечанием будет именно то, что в cbData
указывается размер в байтах, а не в символах. У тебя в lpData
передается строка из wchar_t
, размер которого больше одного байта.bool modifyKey( HKEY root_hey, const std::wstring_view& path, const std::wstring_view& key_name, const std::wstring_view& new_value )
{
HKEY folder_key;
if( RegCreateKeyW( root_hey, path.data(), &folder_key ) != ERROR_SUCCESS )
{
return false;
}
const BYTE* const value_buffer = reinterpret_cast<const BYTE*>( new_value.data() );
const DWORD buffer_size = static_cast<DWORD>( new_value.length() * sizeof( wchar_t ) + 1 );
if( RegSetValueExW( folder_key, key_name.data(), 0, REG_SZ, value_buffer, buffer_size ) != ERROR_SUCCESS )
{
RegCloseKey( folder_key );
return false;
}
if( RegFlushKey( folder_key ) != ERROR_SUCCESS )
{
RegCloseKey( folder_key );
return false;
}
RegCloseKey( folder_key );
return true;
}
SHGetKnownFolderPath
[?].KNOWNFOLDERID
[?].FOLDERID_Desktop
для получения папки рабочего стола пользователя.CoTaskMemFree
[?] для освобождения памяти, переданной из функции через указатель ppszPath
.wchar_t* path_buffer = nullptr;
if( FAILED( ::SHGetKnownFolderPath( FOLDERID_Desktop, KF_FLAG_DEFAULT, 0, &path_buffer ) ) )
{
::CoTaskMemFree( path_buffer );
LOG_ERROR( LOG_CHANNEL, "Failed to get known directory; error - #{:08X}.", ::GetLastError() );
// terminate ...
}
std::wstring user_desktop_path{ path_buffer };
::CoTaskMemFree( path_buffer );
// whatever with `user_desktop_path` ...
new
и сырые указатели. Тебе нужен линейный контейнер с хранением элементов в непрерывном блоке памяти. Это будет или std::vector
, или std::array
.std::span
[?] или gsl::span
[?][S] если ты не можешь пользоваться C++20.gsl::span<float> Метод( gsl::span<float> values )
.span
- это не владеющий памятью тип, обозначающий участок непрерывной памяти с данными определенного типа. span
очень легок и является value type - т.е. создан чтобы его передача по значению не приводила к ощутимым нагрузкам. span
конструируется из std::vector
, std::array
, плоских массивов, std::unique_ptr<[]>
и сырых блоков памяти.values
, считая попутно, сколько элементов ты обработал. А для возврата обработанного участка данных тебе будет достаточно вызвать subspan.std::span<float> Метод( std::span<float> values )
{
size_t processed_count = 0;
for( float& element : values )
{
// ... обработка значений
// ... изменение processed_count
// ... условия обрыва цикла
}
return values.subspan( 0, processed_count );
}
int main()
{
std::vector<float> values{ 7.83f, 14.1f, 20.3f };
std::span<float> processed_values = Метод( values );
for( const float& value : processed_values )
{
std::cout << value << ' ';
}
return 0;
}
A::template rebind<U>::other
), по умолчанию при смене аллоцируемого типа будет заменен первый шаблонный параметр. Был у нас Alloc<Foo, ...>
, станет Alloc<Bar, ...>
.All custom allocators also must be stateless. (until C++11)
enum CellState : uint8_t
уменьшит размер состояния с 4Б до 1Б. А еще этот тип стоит переименовать, т.к. это не CellState
, а что-то относящееся к поведению клетки. А вот CellState
будет выглядеть так:// Renamed from `CellState`.
enum CellBehavior : uint8_t
{
Empty,
Alive,
};
struct CellState final
{
CellBehavior current_behavior : 4;
CellBehavior next_behavior : 4;
};
__stdcall
обязывает пользователя функции передать параметры функции на стеке по значению, но освобождает его чистить стек после вызова. Стек от параметров чистит сама вызванная функция.BOOL (__stdcall *)(HANDLE, PBOOL)
на bool (__stdcall *)(void*, bool*)
компилятор думает об одном размере стека, а код функции - о других.__stdcall
свой результат передает через регистр. регистр используется целочисленный или вещественный. Для целочисленного регистра используется правило продвижения типа. Это означает, что функция, записав значение типа BOOL
(размер 4Б) ничего не испортит пользовательскому коду, который прочитает из регистра все 4Б с учетом правила продвижения.bool*
) передается в использование как указатель на четырехбайтовое целое (BOOL*
или PBOOL
). Вызываемая функция ведь имеет сигнатуру BOOL (__stdcall *)(HANDLE, PBOOL)
и со вторым параметром работает как с 4Б целым по указателю.isWow64
и был поломан в результате вызова IsWow64Process
с параметром неподходящей длины. Измени тип isWow64
на BOOL
и все станет нормально, даже хендл "kernel32.dll"
потом сможешь нормально освободить. glDrawElements
). С точки зрения шейдера, материал - это комплекс из программы шейдера и набора входных/выходных параметров этой программы.