glutPostRedisplay.double GetCurrentTime()
{
using Duration = std::chrono::duration<double>;
return std::chrono::duration_cast<Duration>(
std::chrono::high_resolution_clock::now().time_since_epoch()
).count();
}
const double frame_delay = 1.0 / 60.0; // 60 FPS
double last_render = 0;
void OnIdle()
{
const double current_time = GetCurrentTime();
if( ( current_time - last_render ) > frame_delay )
{
last_render = current_time;
glutPostRedisplay();
}
}double помогает легче находить дельту между кадрами и контролировать ошибку дельты.5 будет являться числовым литералом с типом int.5u, то тип бы уже был unsigned int. А если бы ты написал 5lu, то тип бы уже был unsigned long int. glutIdleFunc. В документации написано что GLUT будет постоянно вызывать переданную функцию в свободное от обработки системных сообщений время. А если в glutIdleFunc передан nullptr (или idle-функция не была задана), то GLUT будет строго ждать следующего системного сообщения.glutPostRedisplay, в glutIdleFunc приведет и к регулярной перерисовке экрана.operator().int N = 0;
bool GreatherThanN(int x) {
if (x > N) {
return true;
}
return false;
}template< int N >
bool GreatherThanN(int x) {
if (x > N) {
return true;
}
return false;
}N в этом случае надо знать на этапе компиляции. Да и в целом, не всё можно определить через параметры шаблона. Параметром шаблона может быть или тип, или константа целочисленного типа или типа сводимого к целочисленному (enum, bool, const char*, известные на этапе компиляции указатели).operator().struct Comparator final
{
int N;
inline const bool operator () ( int x ) const
{
return x > N;
}
};
// ...
Comparator GreatherThanN{ 10 };
// ...
count_if(begin(v), end(v), GreatherThanN);#include <iostream>
using namespace std;
const char ssid[] = "TEST";
const char password[] = "123456";
struct Wifi {
const char *access[2][2];
} wifi{
{
{ ssid, password },
{ "DIR", "654321" }
}
};
int main() {
for (int i = 0; i < sizeof(wifi.access) / sizeof(wifi.access[0]); i++) {
cout << wifi.access[i][0] << ": " << wifi.access[i][1] << '\n';
// TEST: 123456
// DIR: 654321
}
return 0;
}В общем выравниваются в памяти поля по границе кратной своему же размеру. То есть 1-байтовые поля не выравниваются, 2-байтовые — выравниваются на чётные позиции, 4-байтовые — на позиции кратные четырём и т.д.
GetDeterminant() имеет разное поведение для размерности 2 и для остальных размерностей.// Deduction branch. Common determinant calculation.
template< typename TStorage, size_t DIMENSION >
struct DeterminantCalculator final
{
// Or implement the private instantiation for `DeterminantCalculator<TStorage, 1>`.
static_assert( DIMENSION > 2, "Dimension of matrix should be >= 2." );
static inline const TStorage Calculate( const MatrixNxN<TStorage, DIMENSION>& matrix )
{
TStorage result = 0;
for( size_t index = 0; index < DIMENSION; ++index )
{
// ((index & 0)? 1 : -1) --> always -1.
// matrix.GetMinor( 0, 0 ) --> uninitialized result.
// matrix.GetMinor( DIMENSION - 1, 0 ) --> garbage result due to design of `GetMinor`.
result += matrix[ index ] * ((index & 0)? 1 : -1) * matrix.GetMinor( index, 0 ).GetDeterminant();
}
return result;
}
};
// Terminal branch. Determinant for 2x2 matrix.
template< typename TStorage >
struct DeterminantCalculator<TStorage, 2> final
{
static inline const TStorage Calculate( const MatrixNxN<TStorage, 2>& matrix )
{
return matrix[ 0 ] * matrix[ 3 ] - matrix[ 2 ] * matrix[ 1 ];
}
};
template<typename _Type, size_t _Size>
_Type MatrixNxN<_Type, _Size>::GetDeterminant()
{
return DeterminantCalculator<_Type, _Size>::Calculate( *this );
}
template<typename _Type, size_t _Size>
MatrixNxN<_Type, _Size - 1> MatrixNxN<_Type, _Size>::GetMinor(size_t row, size_t col)
{
size_t index = 0;
MatrixNxN<_Type, _Size - 1> result;
// Should be `i < _Size - 1`, since the `result` is `MatrixNxN<_Type, _Size - 1>`.
for (size_t i = 0; i < _Size; i++)
{
if (i == col)
{
// Uninitialized `result` in case of `col == 0`. E.g. ALWAYS.
continue;
}
// Should be `i < _Size - 1`, since the `result` is `MatrixNxN<_Type, _Size - 1>`.
for (size_t j = 0; j < _Size; j++)
{
if (j == row)
{
// Uninitialized `result` in case of `row == 0`.
continue;
}
result[index++] = GetElement(j, i);
}
}
return result;
} swap и посмотреть на то, как компилятор будет генерировать код даже с учетом оптимизации.void swap( int& left, int& right )
{
static int t = left;
left = right;
right = t;
}swap(int&, int&): # @swap(int&, int&)
pushq %r14
pushq %rbx
pushq %rax
movq %rsi, %rbx
movq %rdi, %r14
movb guard variable for swap(int&, int&)::t(%rip), %al
testb %al, %al
je .LBB0_1
.LBB0_3:
movl (%rbx), %eax
movl %eax, (%r14)
movl swap(int&, int&)::t(%rip), %eax
movl %eax, (%rbx)
addq $8, %rsp
popq %rbx
popq %r14
retq
.LBB0_1:
movl $guard variable for swap(int&, int&)::t, %edi
callq __cxa_guard_acquire
testl %eax, %eax
je .LBB0_3
movl (%r14), %eax
movl %eax, swap(int&, int&)::t(%rip)
movl $guard variable for swap(int&, int&)::t, %edi
callq __cxa_guard_release
jmp .LBB0_3void swap( int& left, int& right )
{
int t = left;
left = right;
right = t;
}swap(int&, int&): # @swap(int&, int&)
movl (%rdi), %eax
movl (%rsi), %ecx
movl %ecx, (%rdi)
movl %eax, (%rsi)
retqt объявить именно как глобальную переменную, то сгенерированный код станет немного легче, но он все равно будет объемнее и тяжелее кода с локальной переменной. Уже просто потому что глобальная переменная находится в глобальной памяти, а запросы из процессора в ОЗУ - сравнительно долгая операция.
this->value = NULL;NULL ты надеешься на то что это будет указатель. Далее по тексту ты сам же свою надежду и разбиваешь, инстанцируя свой шаблон от типа int.this->value = T1(); // Или this->value = T1{}; для C++11 и выше.this в этом месте, кстати, можно смело опустить.Type(T1 value) : TypeSize(value)
// ...
Type() : TypeSize()Type(T1 value) : TypeSize<T1>::TypeSize(value)
// ...
Type() : TypeSize<T1>::TypeSize()this->value на value, то компилятор снова скажет про обращение к несуществующему полю. Но если к value обращаться используя его полную квалификацию TypeSize<T1>::value, код соберется должным образом.Type(T1 value) : TypeSize(value)
{
this->value = value;
}
Type() : TypeSize()
{
this->value = 0;
}value из этих конструкторов лучше убрать и предоставить все права по инициализации своих полей типу TypeSize. Плюс, конструктор по умолчанию у тебя снова надеется на простой тривиальный тип. Присвоение нулю приведет к ошибке компиляции уже при инстанцировании для тривиального составного типа.Type(T1 value) : TypeSize<T1>::TypeSize(value)
{
}
Type() : TypeSize<T1>::TypeSize()
{
} template< typename TValueType >
struct Serializer final
{
static inline const bool Read( const BasicStream& stream, TValueType& value_storage );
static inline const bool Write( BasicStream& stream, const TValueType& value_storage );
};template<>
struct Serializer<int8_t> final
{
static inline const bool Read( const BasicStream& stream, int8_t& value_storage );
static inline const bool Write( BasicStream& stream, const int8_t& value_storage );
};
// ...
template<>
struct Serializer<float> final
{
static inline const bool Read( const BasicStream& stream, float& value_storage );
static inline const bool Write( BasicStream& stream, const float& value_storage );
};std::string, и std::vector, и std::map. Если пользователь решит сериализовать весь контейнер своих пользовательских типов, нам не стоит обязывать его писать частное инстанцирование сериализатора для контейнера. Нам надо предусмотреть сериализацию любого стандартного контейнера с любым пользовательским типом.template< typename TCharType, typename TCharTraits, typename TAllocator >
struct Serializer<std::basic_string<TCharType, TCharTraits, TAllocator>> final
{
static inline const bool Read( const BasicStream& stream, std::basic_string<TCharType, TCharTraits, TAllocator>& value_storage );
static inline const bool Write( BasicStream& stream, const std::basic_string<TCharType, TCharTraits, TAllocator>& value_storage );
};template< typename TElement, typename TAllocator >
struct Serializer<std::vector<TElement, TAllocator>> final
{
static inline const bool Read( const BasicStream& stream, std::vector<TElement, TAllocator>& value_storage );
static inline const bool Write( BasicStream& stream, const std::vector<TElement, TAllocator>& value_storage );
};template< typename TLeftElement, typename TRightElement >
struct Serializer<std::pair<TLeftElement, TRightElement>> final
{
static inline const bool Read( const BasicStream& stream, std::pair<TLeftElement, TRightElement>& value_storage );
static inline const bool Write( BasicStream& stream, const std::pair<TLeftElement, TRightElement>& value_storage );
};template< typename TElement, typename TAllocator >
inline const bool Serializer<std::vector<TElement, TAllocator>>::Read(
const BasicStream& stream,
std::vector<TElement, TAllocator>& value_storage
)
{
size32_t stored_size;
Serializer<size32_t>::Read( stream, stored_size );
value_storage.resize( stored_size );
for( TElement& element : value_storage )
{
Serializer<TElement>::Read( stream, element );
}
return true;
}К примеру есть файл .cpp, в нем есть метод, который не относится не к какому пространству
Есть класс, он описан и тд, но я заметил в нем есть "вызова" методов, без всякого описания, то есть даже значения не передаются
size_t min_value_index = 0;
size_t max_value_index = 0;0 для того чтобы изначально обозначить первый же элемент массива и как минимальный, и как максимальный одновременно. Именно так я определю начальное состояние алгоритма.for( size_t index = 1; index < stream_length; ++index )
{
// ...
}for( size_t index = 1; index < stream_length; ++index )
{
if( stream[ index ] < stream[ min_value_index ] )
{
min_value_index = index;
}
if( stream[ index ] > stream[ max_value_index ] )
{
max_value_index = index;
}
}min_value_index будет гарантированно лежать индекс минимального значения массива, а в max_value_index - индекс максимального.(arg / N). Что это означает? Это означает просто целочисленное деление.arg будет эквивалентен N.(int)CycleIteration. Опустим момент что писавший это человек любит "острые ощущения". Обратим внимание только на то, что тут адрес глобальной функции переводится в число.((int)CycleEnd - (int)CycleIteration)). Тип int тут не спроста. Функции CycleEnd и CycleIteration могут быть расположены в произвольных местах бинарного образа. Тут определяется буквальное расстояние между двумя функциями в образе. И это расстояние может быть как положительным, так и отрицательным.ptr будет принимать значение CycleIteration до тех пор, пока arg < N, а когда arg == N, ptr принимает значение CycleEnd. TEST( TestStaticString, GetChar )
{
using TestString = Black::StaticString<'A', 'B', 'C'>;
EXPECT_EQ( 'A', TestString::GetChar( 0 ) );
EXPECT_EQ( 'B', TestString::GetChar( 1 ) );
EXPECT_EQ( 'C', TestString::GetChar( 2 ) );
}TEST - это макрос, раскрывающийся в специальную обвязку твоего теста. Его первый параметр - это имя тестового кейса, второй - имя теста.EXPECT_* и ASSERT_*.VectorTest - это фикстура тестов. Применять ее к своим тестам ты можешь с помощью макроса TEST_F. Прочитать про это можно тут.Еще один момент, который нужно отметить: использование std::remove_reference. На самом деле forward может быть реализован и без использования этой функции. Сжатие ссылок выполнит всю работу, таким образом, применение std::remove_reference для этого избыточно. Однако, эта функция позволяет вывести T& t в ситуации, когда этот тип не может быть выведен (согласно стандарту С++, 14.8.2.5), поэтому необходимо явно указывать параметры шаблона при вызове std::forward.
swap() делает размен состояния между двумя контейнерами.cache. При размене не происходит дополнительных выделений памяти, состояния векторов в буквальном смысле меняются местами.cache с набором из -1.template< typename TInterface, typename... TArguments >
class AbstractFactory final
{
public:
// Produce the implementation, but return the pointer to interface.
inline std::shared_ptr<TInterface> Produce( const std::string& implementation_name, TArguments... arguments )
{
auto found_function = m_factory_functions.find( implementation_name );
return ( found_function == m_factory_functions.end() )? std::shared_ptr<TInterface>{} : found_function->second( std::forward<TArguments>( arguments )... );
};
// Define the implementation.
template< typename TImplementation >
inline const bool DefineImplementation()
{
return DefineImplementation<TImplementation>( TImplementation::ClassName() );
};
// Define the implementation.
template< typename TImplementation >
inline const bool DefineImplementation( const std::string& implementation_name )
{
// Abort the incorrect registration.
static_assert( std::is_base_of<TInterface, TImplementation>::value, "Implementation may only be derived from interface of Factory." );
auto found_function = m_factory_functions.find( implementation_name );
if( found_function == m_factory_functions.end() )
{
m_factory_functions[ implementation_name ] = &AbstractFactory<TInterface, TArguments...>::template ConstructImplementation<TImplementation>;
return true;
};
return false;
};
// Check the implementation name is already defined.
inline const bool IsImplementationDefined( const std::string& implementation_name ) const
{
return m_factory_functions.find( implementation_name ) != m_factory_functions.end();
};
private:
// The factory function just produce implementation.
template< typename TImplementation >
static std::shared_ptr<TInterface> ConstructImplementation( TArguments... arguments )
{
return std::static_pointer_cast<TInterface>(
std::make_shared<TImplementation>( std::forward<TArguments>( arguments )... )
);
};
private:
// Factory function produces the implementations of TInterface.
using FactoryFunction = std::shared_ptr<TInterface> (*)( TArguments... arguments );
std::unordered_map<std::string, FactoryFunction> m_factory_functions;
};