struct Vector3i final
{
int32_t x;
int32_t y;
int32_t z;
};
LineIterator
. Такое имя будет хорошо отражать функциональность.using AxisPointer = int32_t Vector3i::*;
class AxisProxy final
{
public:
AxisProxy( AxisPointer major_axis, AxisPointer middle_axis, AxisPointer minor_axis)
: m_major_axis{ major_axis }
, m_middle_axis{ middle_axis }
, m_minor_axis{ minor_axis }
{}
public:
inline int32_t& AccessMajorAxis( Vector3i& value ) const { return value.*m_major_axis; };
inline int32_t& AccessMiddleAxis( Vector3i& value ) const { return value.*m_middle_axis; };
inline int32_t& AccessMinorAxis( Vector3i& value ) const { return value.*m_minor_axis; };
inline const int32_t& AccessMajorAxis( const Vector3i& value ) const { return value.*m_major_axis; };
inline const int32_t& AccessMiddleAxis( const Vector3i& value ) const { return value.*m_middle_axis; };
inline const int32_t& AccessMinorAxis( const Vector3i& value ) const { return value.*m_minor_axis; };
private:
AxisPointer m_major_axis = &Vector3i::x;
AxisPointer m_middle_axis = &Vector3i::y;
AxisPointer m_minor_axis = &Vector3i::z;
};
class LineIterator final
{
public:
LineIterator( const Vector3i from, const Vector3i to );
public:
const Vector3i& operator ++ ();
const Vector3i operator ++ ( int );
inline const Vector3i& operator * () const { return m_current_point; };
inline const Vector3i* operator -> () const { return &m_current_point; };
private:
static inline const int32_t GetCorrectionStepAxis( const int32_t value ) { return std::abs( value ) << 1; };
static inline const int32_t GetShiftStepAxis( const int32_t value ) { return ( value > 0 ) - ( value < 0 ); };
void PerformLineStep();
private:
Vector3i m_current_point; // Current position at line.
Vector3i m_correction_step; // Values to change the point corrections.
Vector3i m_shift_step; // The shift step for current point in each iteration.
int32_t m_middle_axis_correction; // The marker for middle axis correction.
int32_t m_minor_axis_correction; // The marker for minor axis correction.
AxisProxy m_axis_proxy; // Point fields proxy.
};
const Vector3i& LineIterator::operator ++ ()
{
PerformLineStep();
return m_current_point;
}
const Vector3i LineIterator::operator ++ ( int )
{
Vector3i current_point{ m_current_point };
PerformLineStep();
return current_point;
}
LineIterator::LineIterator( const Vector3i from, const Vector3i to )
: m_current_point{ from }
{
const Vector3i line_delta{ to - from };
m_correction_step = { GetCorrectionStepAxis( line_delta.x ), GetCorrectionStepAxis( line_delta.y ), GetCorrectionStepAxis( line_delta.z ) };
m_shift_step = { GetShiftStepAxis( line_delta.x ), GetShiftStepAxis( line_delta.y ), GetShiftStepAxis( line_delta.z ) };
AxisPointer axis[3] = { &Vector3i::x, &Vector3i::y, &Vector3i::z };
std::sort(
std::begin( axis ), std::end( axis ),
[this]( const AxisPointer left, const AxisPointer right ) -> bool
{
return m_correction_step.*left > m_correction_step.*right;
}
);
m_axis_proxy = { axis[0], axis[1], axis[2] };
m_middle_axis_correction = m_axis_proxy.AccessMiddleAxis( m_correction_step ) - ( m_axis_proxy.AccessMajorAxis( m_correction_step ) >> 1 );
m_minor_axis_correction = m_axis_proxy.AccessMinorAxis( m_correction_step ) - ( m_axis_proxy.AccessMajorAxis( m_correction_step ) >> 1 );
}
m_correction_step
, на базе которого далее производится сортировка указателей на поля осей вектора. Сортируются указатели по убыванию шага их приращения. Именно таким образом определяется главное и вторичное направления. Порядок указателей осей используется для инициализации экземпляра AxisProxy
.void LineIterator::PerformLineStep()
{
if( m_middle_axis_correction > 0 )
{
m_middle_axis_correction -= m_axis_proxy.AccessMajorAxis( m_correction_step );
m_axis_proxy.AccessMiddleAxis( m_current_point ) += m_axis_proxy.AccessMiddleAxis( m_shift_step );
}
if( m_minor_axis_correction > 0 )
{
m_minor_axis_correction -= m_axis_proxy.AccessMajorAxis( m_correction_step );
m_axis_proxy.AccessMinorAxis( m_current_point ) += m_axis_proxy.AccessMinorAxis( m_shift_step );
}
m_middle_axis_correction += m_axis_proxy.AccessMiddleAxis( m_correction_step );
m_minor_axis_correction += m_axis_proxy.AccessMinorAxis( m_correction_step );
m_axis_proxy.AccessMajorAxis( m_current_point ) += m_axis_proxy.AccessMajorAxis( m_shift_step );
}
AxisProxy::m_middle_axis
из кода и удалить весь код, где на него есть ссылки, то весь оставшийся код будет представлять из себя обычный итератор линии для 2D-пространства. В этом случае оси даже сортировать не надо будет, для инициализации прокси можно будет обойтись одним тернарным оператором. ModelMatrix
- это матрица преобразования локального пространства геометрии конкретного объекта.ModelMatrix
.ViewMatrix
- матрица отображения любого внешнего пространства в пространство вида - пространство взора наблюдателя. Наблюдатель (камера) воспринимает объекты только внутри своего собственного локального пространства. Чтобы наблюдатель мог увидеть объект, объект необходимо отобразить из его родного пространства в пространство вида камеры, где он или попадет в область проекции, или не попадет.Projectionmatrix
- матрица отображения видового пространства в пространство проекции вида. Фактически это отображение выполняет проецирование некоторого участка видового пространства на плоскость. Именно отображенная таким образом геометрия становится видимой при презентации кадра.ModelMatrix
.ViewMatrix
.Projectionmatrix
.Projectionmatrix
и ViewMatrix
уникальны для камеры, а вот матрицы ModelMatrix
уникальны уже для каждого объекта отдельно.Projectionmatrix
и ViewMatrix
в процессе презентации кадра являются неизменными, затраты в 48 операций на каждую позицию выглядят как расточительство. Если до презентации выполнить отображение самого пространства вида в пространство проекции, т.е. перемножить ViewMatrix
и Projectionmatrix
в правильном порядке, то с помощью результирующей матрицы viewProjectionMatrix
число операций на одну позицию можно снизить до 32.viewProjectionMatrix
и ModelMatrix
в правильном порядке, то благодаря полученной таким образом MVPMatrix
число операций на одну позицию снизится до изначальных 16.viewProjectionMatrix
и MVPMatrix
просто позволяют снизить трудоемкость презентации кадра. Однако, во время презентации может потребоваться определение положения геометрии в каком-либо промежуточном пространстве, поэтому вдобавок к MVPMatrix
в шейдер принято отдавать и ModelMatrix
, и матрицы камеры.MVPMatrix
бесспорно нужна всегда, то любую другую матрицу в шейдер добавлять стоит только исходя из осмысленной необходимости. Регистры GPU для шейдера не резиновые и могут очень быстро забиться избыточными данными. К тому же, чем меньше на каждом кадре в шейдер передается данных, тем быстрее выполняется презентация кадра. Представьте сложный объект, требующий кропотливой пошаговой инициализации множества полей и вложенных объектов. Код инициализации таких объектов обычно спрятан внутри монструозного конструктора с десятком параметров. Либо ещё хуже — распылён по всему клиентскому коду.
__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);
X, Y, Z
помечаются каналами цветов R, G, B
в однозначном соответствии. Ось X
всегда и везде красная. Ось Y
- зеленая. А ось Z
- синяя.glVertex3f(0, 0,znear);
glVertex3f(0, 0,zfar);
X
и Y
от нуля (хоть на 1pt), измени цвет оси Z
, тогда ее станет немного лучше видно. 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` ...