MyClass.h
и MyClass.cpp
. Когда нужно тематически разделить определение интерфейса, к имени класса после точки и перед расширением файла добавляется суффикс, говорящий о тематике определения. Например MyClass.serialization.cpp
, MyClass.crud.cpp
или MyClass.callbacks.cpp
.template< size_t ROWS, size_t COLUMNS >
struct Matrix final
{
template< size_t R = ROWS, size_t C = COLUMNS >
inline std::enable_if_t<R == C, int> GetDeterminant() const
{
return 0;
}
};
GetDeterminant
не удалось вывести из шаблона? Будет ошибка трансляции, говорящая о том, что метод не найден. Это ничего не говорит пользователю. Это просто инструктирует транслятор остановить трансляцию. Пользователь, особенно если он не искушен знаниями о SFINAE, не сможет понять причину ошибки трансляции. Такая ситуация создаст риск излишней траты времени на дознание причин ошибки.template< size_t ROWS, size_t COLUMNS >
struct Matrix final
{
template< size_t R = ROWS, size_t C = COLUMNS >
inline std::enable_if_t<R != C, int> GetDeterminant() const = delete;
template< size_t R = ROWS, size_t C = COLUMNS >
inline std::enable_if_t<R == C, int> GetDeterminant() const
{
return 0;
}
};
GetDeterminant
.template< size_t ROWS, size_t COLUMNS >
struct Matrix final
{
inline int GetDeterminant() const
{
static_assert( ROWS == COLUMNS, "Matrix should be square to calculate the determinant." );
return 0;
}
};
ArrayArray[0] = *new Array<int>{10};
ArrayArray[0]
вернет ссылку на Array<int>
.*new Array<int>{10}
выделяет память в куче под Array<int>
, вызывает инициализатор Array<int>::Array(int length)
, после чего делает разыменование получившегося указателя на Array<int>
.Array<int>
будет выполнен оператор копирования по умолчанию, функциональность которого просто скопирует поля из объекта справа в объект слева от присвоения.new Array<int>{10}
становится утекшим, т.к. указатель на него сразу становится потерян и освободить занимаемую им память становится невозможно.ArrayArray[0] = Array<int>{10};
ArrayArray[0]
вернет ссылку на Array<int>
.Array<int>{10}
инициализирует безымянный локальный объект на стеке, используя инициализатор Array<int>::Array(int length)
.T *m_data
.ArrayArray[0]
в этот момент начинает ссылаться на освобожденную память.ArrayArray
в конце работы программы пытается удалить уже освобожденную память в ArrayArray[0]
.resize
[?].insert
[?].vertexBuffer.insert( vertexBuffer.begin() + 1, x );
Только хотелось бы без специализации, чтобы код не дублировать
std::enable_if
[?]. Часто его используют для выбора поведения шаблона исходя из черт аргумента шаблона.соответственно во всех методах, где этот член используется можно будет проверку if constexpr (someCondition)
if constexpr
, нужно убирать в функции. А где эти функции лучше расположить? Верно - прямо там, где для их работы определены данные. Поэтому на самом деле if constexpr
не нужен. Нужно определить набор функций с поведением там где оно возможно. А где нет - определить заглушки чтобы клиентский, относительно вариативного поведения, код не нуждался в проверках и мог просто обращаться к вариативному поведению так, как будто оно не вариативно и есть всегда.DebugName
[?], в котором такие заглушки реализованы. *reinterpret_cast<T*>( &buffer[ sizeof( T ) * head ] ) = T{ std::forward<Args>( args )... };
auto T vicar{ std::forward<Args>( args )... };
std::swap( *reinterpret_cast<T*>( &buffer[ sizeof( T ) * head ] ), vicar );
std::swap
, но только от людей со слабой привычкой пользоваться STL.T
является неперемещаемым. В твоем ЦБ должны присутствовать проверки на перемещаемость, копируемость и возможность размена состояний.T
можно копировать, но не перемещать, использовать стоит оператор копирования.T
нельзя, следует пользоваться деструктором и размещающим конструктором.T
включалась только одна конкретная перегрузка шаблона. BitmapData data = bitmap2.LockBits(new System.Drawing.Rectangle(x, y, weight, height),
ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppRgb);
data
будет XRGB с шириной 32 бита.GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, data.Width, data.Height, 0,
OpenTK.Graphics.OpenGL.PixelFormat.Rgba, PixelType.UnsignedByte, data.Scan0);
PixelInternalFormat.Rgba
означает что в памяти GPU текстура будет представлена в формате RGBA с каналами float
с нормализацией.OpenTK.Graphics.OpenGL.PixelFormat.Rgba
означает что формат data.Scan0
нужно воспринимать как RGBA, а PixelType.UnsignedByte
означает что каналы в data.Scan0
нужно воспринимать как unsigned byte
.XRGB != RGBA
.Format32bppArgb
. Но в этом случае есть вероятность ошибиться с порядком каналов между Format32bppArgb
и OpenTK.Graphics.OpenGL.PixelFormat.Rgba
..cpp
обычно являются точками сборки модулей трансляции [?]..cpp
подключить другой .cpp
(исключенный из сборки иными способами), то все элементы с внутренним связыванием любого из этих .cpp
будут доступны в них обоих..h
. Файлы .cpp
обычно включают .h
и все вместе своим кодом формируют модуль трансляции, в котором доступны все элементы с внутренним связыванием. Даже в коде файлов .h
.static
) элементы именованных пространств имен по умолчанию имеют внешнее связывание [?]. Глобальное пространство является тоже именованным (и доступно через ::
без имени слева) и ровно так же наделяет все свои нестатические элементы характеристикой внешнего связывания.inline
.inline
[?] дает пометку слабого внешнего связывания для любой сущности. Что константа или глобальная переменная, что функция или метод (даже статический), помечаются как сущности с внешним связыванием, которое не нарушает ODR в случае если все определения цели связывания во всех модулях трансляции являются полностью одинаковыми. Если хоть одно определение цели связывания отличается - будет нарушение ODR..cpp
определить inline
функции с одним именем и одной сигнатурой, но разными телами, то проблем со сборкой будет не избежать.A declaration whose declarator-id is an operator-function-id shall declare a function or function template or an explicit instantiation or specialization of a function template. A function so declared is an operator function.
cout << a.get() << b.get();
std::ostream& operator << ( std::ostream&, int )
.operator << ()
стандарт говорит что это перегрузка в форме внешней функции.operator<<( operator<<( cout, a.get() ), b.get() );
The evaluations of the postfix expression and of the arguments are all unsequenced relative to one another. All side effects of argument evaluations are sequenced before the function is entered (see 1.9).
If an operator function is invoked using operator notation, argument evaluation is sequenced as specified for the built-in operator; see 16.3.1.2.
cout << a.get() << b.get();
можно упростить до cout << ++i << ++i;
, что уже более явно должно показывать наличие UB.2) The value computations (but not the side-effects) of the operands to any operator are sequenced before the value computation of the result of the operator (but not its side-effects).
3) When calling a function (whether or not the function is inline, and whether or not explicit function call syntax is used), every value computation and side effect associated with any argument expression, or with the postfix expression designating the called function, is sequenced before execution of every expression or statement in the body of the called function.
5) The side effect of the built-in pre-increment and pre-decrement operators is sequenced before its value computation (implicit rule due to definition as compound assignment)
16) Every overloaded operator obeys the sequencing rules of the built-in operator it overloads when called using operator notation. (since C++17)
19) In a shift operator expressionE1<<E2
andE1>>E2
, every value computation and side-effect of E1 is sequenced before every value computation and side effect of E2. (since C++17)
cout << a.get() << b.get();
не определен, но поведение этого кода определено. Поэтому при трансляции по стандарту C++14 этот код может выдать или 12
, или 21
. Но не 11
.cout << a.get() << b.get();
всегда однозначен. При трансляции этого кода по стандарту C++17 (и дальше) в консоль будет выведено всегда и только 12
.cout << a.get() << b.get();
не определено.1
имеет категорию prvalue. 1
является литералом и не является строковым литералом. Это - литерал с типом int
по умолчанию.const int& ref = 1;
или int&& ref = 1;
является полностью стандартным.ref
в этом случае будет ссылаться на переданный литерал и отсутствие размещения литерала этому не помеха.void foo( const int& ref );
void bar()
{
foo( 1 );
}
const int &ref=1;
можно найти в том, чтобы не писать магические константы в коде. ref
- очень плохое имя. Но голая 1
в коде еще хуже.const &y = x;
int
. Если в C код const y = x
является синтаксически верным и подразумевает const int y = x
, то в C++ этот же код является уже синтаксически неверным и не пройдет трансляцию.glMap1
[?] имеет довольно узкий коридор поддержки. Она была введена в OpenGL 1.0 и удалена из поддержки в OpenGL 3.2 Core Profile.wglCreateContext
, система сама решает какой контекст ей для тебя создавать. Это может быть и контекст с версией 4.6, в котором уже нет поддержки функции glMap1f
.wglCreateContextAttribsARB
позволяет задавать атрибуты для создаваемого контекста, среди которых ты можешь обозначить и требуемую версию.// Sample code showing how to create a modern OpenGL window and rendering context on Win32.
#include <windows.h>
#include <gl/gl.h>
#include <stdbool.h>
typedef HGLRC WINAPI wglCreateContextAttribsARB_type(HDC hdc, HGLRC hShareContext,
const int *attribList);
wglCreateContextAttribsARB_type *wglCreateContextAttribsARB;
// See https://www.khronos.org/registry/OpenGL/extensions/ARB/WGL_ARB_create_context.txt for all values
#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
typedef BOOL WINAPI wglChoosePixelFormatARB_type(HDC hdc, const int *piAttribIList,
const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats);
wglChoosePixelFormatARB_type *wglChoosePixelFormatARB;
// See https://www.khronos.org/registry/OpenGL/extensions/ARB/WGL_ARB_pixel_format.txt for all values
#define WGL_DRAW_TO_WINDOW_ARB 0x2001
#define WGL_ACCELERATION_ARB 0x2003
#define WGL_SUPPORT_OPENGL_ARB 0x2010
#define WGL_DOUBLE_BUFFER_ARB 0x2011
#define WGL_PIXEL_TYPE_ARB 0x2013
#define WGL_COLOR_BITS_ARB 0x2014
#define WGL_DEPTH_BITS_ARB 0x2022
#define WGL_STENCIL_BITS_ARB 0x2023
#define WGL_FULL_ACCELERATION_ARB 0x2027
#define WGL_TYPE_RGBA_ARB 0x202B
static void
fatal_error(char *msg)
{
MessageBoxA(NULL, msg, "Error", MB_OK | MB_ICONEXCLAMATION);
exit(EXIT_FAILURE);
}
static void
init_opengl_extensions(void)
{
// Before we can load extensions, we need a dummy OpenGL context, created using a dummy window.
// We use a dummy window because you can only set the pixel format for a window once. For the
// real window, we want to use wglChoosePixelFormatARB (so we can potentially specify options
// that aren't available in PIXELFORMATDESCRIPTOR), but we can't load and use that before we
// have a context.
WNDCLASSA window_class = {
.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC,
.lpfnWndProc = DefWindowProcA,
.hInstance = GetModuleHandle(0),
.lpszClassName = "Dummy_WGL_djuasiodwa",
};
if (!RegisterClassA(&window_class)) {
fatal_error("Failed to register dummy OpenGL window.");
}
HWND dummy_window = CreateWindowExA(
0,
window_class.lpszClassName,
"Dummy OpenGL Window",
0,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
0,
0,
window_class.hInstance,
0);
if (!dummy_window) {
fatal_error("Failed to create dummy OpenGL window.");
}
HDC dummy_dc = GetDC(dummy_window);
PIXELFORMATDESCRIPTOR pfd = {
.nSize = sizeof(pfd),
.nVersion = 1,
.iPixelType = PFD_TYPE_RGBA,
.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
.cColorBits = 32,
.cAlphaBits = 8,
.iLayerType = PFD_MAIN_PLANE,
.cDepthBits = 24,
.cStencilBits = 8,
};
int pixel_format = ChoosePixelFormat(dummy_dc, &pfd);
if (!pixel_format) {
fatal_error("Failed to find a suitable pixel format.");
}
if (!SetPixelFormat(dummy_dc, pixel_format, &pfd)) {
fatal_error("Failed to set the pixel format.");
}
HGLRC dummy_context = wglCreateContext(dummy_dc);
if (!dummy_context) {
fatal_error("Failed to create a dummy OpenGL rendering context.");
}
if (!wglMakeCurrent(dummy_dc, dummy_context)) {
fatal_error("Failed to activate dummy OpenGL rendering context.");
}
wglCreateContextAttribsARB = (wglCreateContextAttribsARB_type*)wglGetProcAddress(
"wglCreateContextAttribsARB");
wglChoosePixelFormatARB = (wglChoosePixelFormatARB_type*)wglGetProcAddress(
"wglChoosePixelFormatARB");
wglMakeCurrent(dummy_dc, 0);
wglDeleteContext(dummy_context);
ReleaseDC(dummy_window, dummy_dc);
DestroyWindow(dummy_window);
}
static HGLRC
init_opengl(HDC real_dc)
{
init_opengl_extensions();
// Now we can choose a pixel format the modern way, using wglChoosePixelFormatARB.
int pixel_format_attribs[] = {
WGL_DRAW_TO_WINDOW_ARB, GL_TRUE,
WGL_SUPPORT_OPENGL_ARB, GL_TRUE,
WGL_DOUBLE_BUFFER_ARB, GL_TRUE,
WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
WGL_COLOR_BITS_ARB, 32,
WGL_DEPTH_BITS_ARB, 24,
WGL_STENCIL_BITS_ARB, 8,
0
};
int pixel_format;
UINT num_formats;
wglChoosePixelFormatARB(real_dc, pixel_format_attribs, 0, 1, &pixel_format, &num_formats);
if (!num_formats) {
fatal_error("Failed to set the OpenGL 3.3 pixel format.");
}
PIXELFORMATDESCRIPTOR pfd;
DescribePixelFormat(real_dc, pixel_format, sizeof(pfd), &pfd);
if (!SetPixelFormat(real_dc, pixel_format, &pfd)) {
fatal_error("Failed to set the OpenGL 3.3 pixel format.");
}
// Specify that we want to create an OpenGL 3.3 core profile context
int gl33_attribs[] = {
WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
WGL_CONTEXT_MINOR_VERSION_ARB, 3,
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
0,
};
HGLRC gl33_context = wglCreateContextAttribsARB(real_dc, 0, gl33_attribs);
if (!gl33_context) {
fatal_error("Failed to create OpenGL 3.3 context.");
}
if (!wglMakeCurrent(real_dc, gl33_context)) {
fatal_error("Failed to activate OpenGL 3.3 rendering context.");
}
return gl33_context;
}
static LRESULT CALLBACK
window_callback(HWND window, UINT msg, WPARAM wparam, LPARAM lparam)
{
LRESULT result = 0;
switch (msg) {
case WM_CLOSE:
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
result = DefWindowProcA(window, msg, wparam, lparam);
break;
}
return result;
}
static HWND
create_window(HINSTANCE inst)
{
WNDCLASSA window_class = {
.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC,
.lpfnWndProc = window_callback,
.hInstance = inst,
.hCursor = LoadCursor(0, IDC_ARROW),
.hbrBackground = 0,
.lpszClassName = "WGL_fdjhsklf",
};
if (!RegisterClassA(&window_class)) {
fatal_error("Failed to register window.");
}
// Specify a desired width and height, then adjust the rect so the window's client area will be
// that size.
RECT rect = {
.right = 1024,
.bottom = 576,
};
DWORD window_style = WS_OVERLAPPEDWINDOW;
AdjustWindowRect(&rect, window_style, false);
HWND window = CreateWindowExA(
0,
window_class.lpszClassName,
"OpenGL",
window_style,
CW_USEDEFAULT,
CW_USEDEFAULT,
rect.right - rect.left,
rect.bottom - rect.top,
0,
0,
inst,
0);
if (!window) {
fatal_error("Failed to create window.");
}
return window;
}
int WINAPI
WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmd_line, int show)
{
HWND window = create_window(inst);
HDC gldc = GetDC(window);
HGLRC glrc = init_opengl(gldc);
ShowWindow(window, show);
UpdateWindow(window);
bool running = true;
while (running) {
MSG msg;
while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT) {
running = false;
} else {
TranslateMessage(&msg);
DispatchMessageA(&msg);
}
}
glClearColor(1.0f, 0.5f, 0.5f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Do OpenGL rendering here
SwapBuffers(gldc);
}
return 0;
}