Until I hit a wall What if I want to create a variable at runtime? Static can only be declared at compile time.... mmm....
Then I understood the truth, and the truth made me free curse, pointers are perfect to create structures and objects dynamically at runtime(with new and delete), it is very similar to declare a non-existent variable at runtime, it is like creating a program inside the program.
Well that's my personal experience, what experience do you have regarding the use of pointers?
...
A useful example of its use, in c++98 (this small program creates .bin files and reads them, it was created taking into account the creation of objects at runtime):
Code: Select all
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <fstream>
#include <cstdlib>
#include <cstring>
#include <limits>
#include <dirent.h> // For file listing (POSIX systems)
// Fundamental data types
enum DataType {
BOOL,
CHAR,
SIGNED_CHAR,
UNSIGNED_CHAR,
SHORT,
UNSIGNED_SHORT,
INT,
UNSIGNED_INT,
LONG,
UNSIGNED_LONG,
FLOAT,
DOUBLE,
LONG_DOUBLE,
STRING
};
// Structure to store inventory data
struct RPGData {
DataType type;
std::string label;
bool has_value;
union {
bool bool_val;
char char_val;
signed char signed_char_val;
unsigned char unsigned_char_val;
short short_val;
unsigned short unsigned_short_val;
int int_val;
unsigned int unsigned_int_val;
long long_val;
unsigned long unsigned_long_val;
float float_val;
double double_val;
long double long_double_val;
};
std::string string_val; // For string type
RPGData(DataType t, const std::string &l)
: type(t), label(l), has_value(false) { }
};
class RPGInventory {
private:
std::vector<RPGData> items;
const std::string BIN_DIR;
// Template function for string to T conversion
template<typename T>
bool convert(const std::string& str, T& value) {
std::istringstream iss(str);
return !(iss >> value).fail();
}
// Specialization for char
bool convert(const std::string& str, char& value) {
if(str.length() == 1) {
value = str[0];
return true;
}
return false;
}
public:
RPGInventory() : BIN_DIR("binoutputs/") { }
size_t getItemCount() const {
return items.size();
}
bool addItem(const std::string &label, DataType type, const std::string &value) {
RPGData item(type, label);
if(!value.empty()){
bool success = true;
switch(type) {
case BOOL: success = convert(value, item.bool_val); break;
case CHAR: success = convert(value, item.char_val); break;
case SIGNED_CHAR: success = convert(value, item.signed_char_val); break;
case UNSIGNED_CHAR:success = convert(value, item.unsigned_char_val); break;
case SHORT: success = convert(value, item.short_val); break;
case UNSIGNED_SHORT: success = convert(value, item.unsigned_short_val); break;
case INT: success = convert(value, item.int_val); break;
case UNSIGNED_INT: success = convert(value, item.unsigned_int_val); break;
case LONG: success = convert(value, item.long_val); break;
case UNSIGNED_LONG: success = convert(value, item.unsigned_long_val); break;
case FLOAT: success = convert(value, item.float_val); break;
case DOUBLE: success = convert(value, item.double_val); break;
case LONG_DOUBLE: success = convert(value, item.long_double_val); break;
case STRING: item.string_val = value; break;
default: success = false; break;
}
if(!success)
return false;
item.has_value = true;
}
items.push_back(item);
return true;
}
bool modifyItem(const std::string &label, const std::string &new_value) {
for(size_t i = 0; i < items.size(); i++){
if(items[i].label == label) {
bool success = true;
switch(items[i].type) {
case BOOL: success = convert(new_value, items[i].bool_val); break;
case CHAR: success = convert(new_value, items[i].char_val); break;
case SIGNED_CHAR: success = convert(new_value, items[i].signed_char_val); break;
case UNSIGNED_CHAR:success = convert(new_value, items[i].unsigned_char_val); break;
case SHORT: success = convert(new_value, items[i].short_val); break;
case UNSIGNED_SHORT: success = convert(new_value, items[i].unsigned_short_val); break;
case INT: success = convert(new_value, items[i].int_val); break;
case UNSIGNED_INT: success = convert(new_value, items[i].unsigned_int_val); break;
case LONG: success = convert(new_value, items[i].long_val); break;
case UNSIGNED_LONG: success = convert(new_value, items[i].unsigned_long_val); break;
case FLOAT: success = convert(new_value, items[i].float_val); break;
case DOUBLE: success = convert(new_value, items[i].double_val); break;
case LONG_DOUBLE: success = convert(new_value, items[i].long_double_val); break;
case STRING: items[i].string_val = new_value; break;
default: success = false; break;
}
if(!success)
return false;
items[i].has_value = true;
return true;
}
}
return false;
}
void display() const {
std::cout << "\nInventory:" << std::endl;
for(size_t i = 0; i < items.size(); ++i) {
const RPGData &item = items[i];
std::cout << item.label << " [";
switch(item.type) {
case BOOL: std::cout << "bool"; break;
case CHAR: std::cout << "char"; break;
case SIGNED_CHAR: std::cout << "signed char"; break;
case UNSIGNED_CHAR: std::cout << "unsigned char"; break;
case SHORT: std::cout << "short"; break;
case UNSIGNED_SHORT: std::cout << "unsigned short"; break;
case INT: std::cout << "int"; break;
case UNSIGNED_INT: std::cout << "unsigned int"; break;
case LONG: std::cout << "long"; break;
case UNSIGNED_LONG: std::cout << "unsigned long"; break;
case FLOAT: std::cout << "float"; break;
case DOUBLE: std::cout << "double"; break;
case LONG_DOUBLE: std::cout << "long double"; break;
case STRING: std::cout << "string"; break;
}
std::cout << "]: ";
if(!item.has_value)
std::cout << "NULL";
else {
switch(item.type) {
case BOOL: std::cout << (item.bool_val ? "true" : "false"); break;
case CHAR: std::cout << "'" << item.char_val << "'"; break;
case SIGNED_CHAR: std::cout << static_cast<int>(item.signed_char_val); break;
case UNSIGNED_CHAR: std::cout << static_cast<unsigned>(item.unsigned_char_val); break;
case SHORT: std::cout << item.short_val; break;
case UNSIGNED_SHORT: std::cout << item.unsigned_short_val; break;
case INT: std::cout << item.int_val; break;
case UNSIGNED_INT: std::cout << item.unsigned_int_val; break;
case LONG: std::cout << item.long_val; break;
case UNSIGNED_LONG: std::cout << item.unsigned_long_val; break;
case FLOAT: std::cout << item.float_val; break;
case DOUBLE: std::cout << item.double_val; break;
case LONG_DOUBLE: std::cout << item.long_double_val; break;
case STRING: std::cout << "\"" << item.string_val << "\""; break;
}
}
std::cout << std::endl;
}
}
std::string getFullFilename(const std::string &filename) const {
std::string full = filename;
if(full.size() < 4 || full.substr(full.size()-4) != ".bin") {
full += ".bin";
}
return BIN_DIR + full;
}
void saveToFile(const std::string &filename) {
std::string fullFileName = getFullFilename(filename);
std::ofstream file(fullFileName.c_str(), std::ios::binary);
if(!file) {
std::cout << "Error opening file for saving." << std::endl;
return;
}
size_t count = items.size();
file.write(reinterpret_cast<const char*>(&count), sizeof(size_t));
for(size_t i = 0; i < items.size(); ++i) {
const RPGData &item = items[i];
file.write(reinterpret_cast<const char*>(&item.type), sizeof(DataType));
size_t label_len = item.label.size();
file.write(reinterpret_cast<const char*>(&label_len), sizeof(size_t));
file.write(item.label.c_str(), label_len);
file.write(reinterpret_cast<const char*>(&item.has_value), sizeof(bool));
if(item.has_value) {
if(item.type == STRING) {
size_t str_len = item.string_val.size();
file.write(reinterpret_cast<const char*>(&str_len), sizeof(size_t));
file.write(item.string_val.c_str(), str_len);
} else {
switch(item.type) {
case BOOL: file.write(reinterpret_cast<const char*>(&item.bool_val), sizeof(bool)); break;
case CHAR: file.write(reinterpret_cast<const char*>(&item.char_val), sizeof(char)); break;
case SIGNED_CHAR: file.write(reinterpret_cast<const char*>(&item.signed_char_val), sizeof(signed char)); break;
case UNSIGNED_CHAR: file.write(reinterpret_cast<const char*>(&item.unsigned_char_val), sizeof(unsigned char)); break;
case SHORT: file.write(reinterpret_cast<const char*>(&item.short_val), sizeof(short)); break;
case UNSIGNED_SHORT: file.write(reinterpret_cast<const char*>(&item.unsigned_short_val), sizeof(unsigned short)); break;
case INT: file.write(reinterpret_cast<const char*>(&item.int_val), sizeof(int)); break;
case UNSIGNED_INT: file.write(reinterpret_cast<const char*>(&item.unsigned_int_val), sizeof(unsigned int)); break;
case LONG: file.write(reinterpret_cast<const char*>(&item.long_val), sizeof(long)); break;
case UNSIGNED_LONG: file.write(reinterpret_cast<const char*>(&item.unsigned_long_val), sizeof(unsigned long)); break;
case FLOAT: file.write(reinterpret_cast<const char*>(&item.float_val), sizeof(float)); break;
case DOUBLE: file.write(reinterpret_cast<const char*>(&item.double_val), sizeof(double)); break;
case LONG_DOUBLE: file.write(reinterpret_cast<const char*>(&item.long_double_val), sizeof(long double)); break;
default: break;
}
}
}
}
std::cout << "Saved " << count << " items to " << fullFileName << std::endl;
}
void loadFromFile(const std::string &filename) {
std::string fullFileName = getFullFilename(filename);
std::ifstream file(fullFileName.c_str(), std::ios::binary);
if(!file) {
std::cout << "Error opening file for loading." << std::endl;
return;
}
items.clear();
size_t count = 0;
file.read(reinterpret_cast<char*>(&count), sizeof(size_t));
for(size_t i = 0; i < count; ++i) {
RPGData item(BOOL, "");
file.read(reinterpret_cast<char*>(&item.type), sizeof(DataType));
size_t label_len = 0;
file.read(reinterpret_cast<char*>(&label_len), sizeof(size_t));
char *label = new char[label_len + 1];
file.read(label, label_len);
label[label_len] = '\0';
item.label = label;
delete[] label;
file.read(reinterpret_cast<char*>(&item.has_value), sizeof(bool));
if(item.has_value) {
if(item.type == STRING) {
size_t str_len = 0;
file.read(reinterpret_cast<char*>(&str_len), sizeof(size_t));
char *str = new char[str_len + 1];
file.read(str, str_len);
str[str_len] = '\0';
item.string_val = str;
delete[] str;
} else {
switch(item.type) {
case BOOL: file.read(reinterpret_cast<char*>(&item.bool_val), sizeof(bool)); break;
case CHAR: file.read(reinterpret_cast<char*>(&item.char_val), sizeof(char)); break;
case SIGNED_CHAR: file.read(reinterpret_cast<char*>(&item.signed_char_val), sizeof(signed char)); break;
case UNSIGNED_CHAR: file.read(reinterpret_cast<char*>(&item.unsigned_char_val), sizeof(unsigned char)); break;
case SHORT: file.read(reinterpret_cast<char*>(&item.short_val), sizeof(short)); break;
case UNSIGNED_SHORT: file.read(reinterpret_cast<char*>(&item.unsigned_short_val), sizeof(unsigned short)); break;
case INT: file.read(reinterpret_cast<char*>(&item.int_val), sizeof(int)); break;
case UNSIGNED_INT: file.read(reinterpret_cast<char*>(&item.unsigned_int_val), sizeof(unsigned int)); break;
case LONG: file.read(reinterpret_cast<char*>(&item.long_val), sizeof(long)); break;
case UNSIGNED_LONG: file.read(reinterpret_cast<char*>(&item.unsigned_long_val), sizeof(unsigned long)); break;
case FLOAT: file.read(reinterpret_cast<char*>(&item.float_val), sizeof(float)); break;
case DOUBLE: file.read(reinterpret_cast<char*>(&item.double_val), sizeof(double)); break;
case LONG_DOUBLE: file.read(reinterpret_cast<char*>(&item.long_double_val), sizeof(long double)); break;
default: break;
}
}
}
items.push_back(item);
}
std::cout << "Loaded " << count << " items from " << fullFileName << std::endl;
}
void listFiles() const {
DIR *dir = opendir(BIN_DIR.c_str());
if(!dir) {
std::cout << "Could not open directory " << BIN_DIR << std::endl;
return;
}
std::cout << "\nSaved bin files:" << std::endl;
struct dirent *entry;
bool found = false;
while((entry = readdir(dir)) != NULL) {
std::string fname = entry->d_name;
if(fname == "." || fname == "..") continue;
if(fname.size() >= 4 && fname.substr(fname.size()-4) == ".bin") {
std::cout << " - " << fname << std::endl;
found = true;
}
}
if(!found) std::cout << " (No saved files)" << std::endl;
closedir(dir);
}
};
int main() {
std::cout << "# Program Start" << std::endl;
std::cout << "RPG Inventory System" << std::endl;
std::cout << "Commands:" << std::endl;
std::cout << " add <label> <type> [value]" << std::endl;
std::cout << " display" << std::endl;
std::cout << " save <filename>" << std::endl;
std::cout << " load <filename>" << std::endl;
std::cout << " list" << std::endl;
std::cout << " modify <label> <new_value>" << std::endl;
std::cout << " exit" << std::endl << std::endl;
RPGInventory inv;
std::string command;
while(true) {
std::cout << "> ";
std::getline(std::cin, command);
if(command.empty()) continue;
std::istringstream iss(command);
std::string action;
iss >> action;
if(action == "exit") {
break;
} else if(action == "add") {
std::string label, type;
if(!(iss >> label >> type)) {
std::cout << "Invalid command!" << std::endl;
continue;
}
DataType dt;
bool validType = true;
if(type == "bool") dt = BOOL;
else if(type == "char") dt = CHAR;
else if(type == "signed_char") dt = SIGNED_CHAR;
else if(type == "unsigned_char") dt = UNSIGNED_CHAR;
else if(type == "short") dt = SHORT;
else if(type == "unsigned_short") dt = UNSIGNED_SHORT;
else if(type == "int") dt = INT;
else if(type == "unsigned_int") dt = UNSIGNED_INT;
else if(type == "long") dt = LONG;
else if(type == "unsigned_long") dt = UNSIGNED_LONG;
else if(type == "float") dt = FLOAT;
else if(type == "double") dt = DOUBLE;
else if(type == "long_double") dt = LONG_DOUBLE;
else if(type == "string") dt = STRING;
else validType = false;
std::string value;
std::getline(iss >> std::ws, value);
if(!validType) {
std::cout << "Invalid command!" << std::endl;
continue;
}
bool result = inv.addItem(label, dt, value);
if(!result) std::cout << "Invalid command!" << std::endl;
else {
std::cout << "Added " << label;
if(value.empty()) std::cout << " (NULL)" << std::endl;
else std::cout << " = " << value << std::endl;
}
} else if(action == "display") {
inv.display();
} else if(action == "save") {
std::string filename;
if(!(iss >> filename)) {
std::cout << "Invalid command!" << std::endl;
continue;
}
inv.saveToFile(filename);
} else if(action == "load") {
std::string filename;
if(!(iss >> filename)) {
std::cout << "Invalid command!" << std::endl;
continue;
}
inv.loadFromFile(filename);
} else if(action == "list") {
inv.listFiles();
} else if(action == "modify") {
std::string label;
if(!(iss >> label)) {
std::cout << "Invalid command!" << std::endl;
continue;
}
std::string new_value;
std::getline(iss >> std::ws, new_value);
if(new_value.empty()){
std::cout << "Invalid command!" << std::endl;
continue;
}
bool result = inv.modifyItem(label, new_value);
if(result) std::cout << "Modified " << label << std::endl;
else std::cout << "Invalid command!" << std::endl;
} else {
std::cout << "Invalid command!" << std::endl;
}
}
return 0;
}



Storing binary values can be useful for storing results at runtime and replicating them as they are loaded into memory and read (considerably reducing the load on the CPU, but increasing the load time at the start of such functionality).
The next example the result resembles loading images into ram memory and playing them back, this could be assigned to events and displayed depending on the relative distance, etc, although for them you have to work with a coordinate system that adapts to that system... that is, precompute using the cache. In PS2 games for example the cache was used to precompute the camera movement:
Code: Select all
#include <windows.h>
#define IMAGE_WIDTH 256
#define IMAGE_HEIGHT 256
#define MAX_FILES 80
struct Pixel {
int x, y;
BYTE r, g, b;
};
struct ImageData {
Pixel pixels[IMAGE_WIDTH * IMAGE_HEIGHT]; // Maximum space for pixels
int count;
};
// Global variables
ImageData images[MAX_FILES];
HBITMAP bitmaps[MAX_FILES];
int currentFileIndex = 0;
const char* folderPath = "./binoutputs/";
HWND hwnd;
// Function to load a bin file into memory
bool loadBinFile(int index, ImageData& img) {
char filePath[MAX_PATH];
wsprintf(filePath, "%s%04d.bin", folderPath, index); // 0001.bin, 0002.bin, etc.
HANDLE hFile = CreateFile(filePath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) return false;
DWORD bytesRead;
img.count = 0;
while (ReadFile(hFile, &img.pixels[img.count].x, sizeof(int), &bytesRead, NULL) && bytesRead > 0 &&
ReadFile(hFile, &img.pixels[img.count].y, sizeof(int), &bytesRead, NULL) && bytesRead > 0 &&
ReadFile(hFile, &img.pixels[img.count].r, sizeof(BYTE), &bytesRead, NULL) && bytesRead > 0 &&
ReadFile(hFile, &img.pixels[img.count].g, sizeof(BYTE), &bytesRead, NULL) && bytesRead > 0 &&
ReadFile(hFile, &img.pixels[img.count].b, sizeof(BYTE), &bytesRead, NULL) && bytesRead > 0) {
img.count++;
if (img.count >= IMAGE_WIDTH * IMAGE_HEIGHT) break;
}
CloseHandle(hFile);
return true;
}
// Function to draw image to bitmap
void drawImageToBitmap(HDC hdc, const ImageData& img) {
for (int i = 0; i < img.count; i++) {
SetPixel(hdc, img.pixels[i].x, img.pixels[i].y, RGB(img.pixels[i].r, img.pixels[i].g, img.pixels[i].b));
}
}
// Function to create bitmap from image data
HBITMAP createBitmapFromImage(const ImageData& img) {
HDC hdcScreen = GetDC(NULL);
HDC hdcMem = CreateCompatibleDC(hdcScreen);
HBITMAP hbm = CreateCompatibleBitmap(hdcScreen, IMAGE_WIDTH, IMAGE_HEIGHT);
HBITMAP hbmOld = (HBITMAP)SelectObject(hdcMem, hbm);
// Initialize bitmap with black background
HBRUSH hbrBackground = CreateSolidBrush(RGB(0, 0, 0)); // Black background
RECT rect = { 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT };
FillRect(hdcMem, &rect, hbrBackground);
DeleteObject(hbrBackground);
drawImageToBitmap(hdcMem, img);
SelectObject(hdcMem, hbmOld);
DeleteDC(hdcMem);
ReleaseDC(NULL, hdcScreen);
return hbm;
}
// Window procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
static int windowWidth = IMAGE_WIDTH;
static int windowHeight = IMAGE_HEIGHT;
static int bitmapWidth = IMAGE_WIDTH;
static int bitmapHeight = IMAGE_HEIGHT;
switch (msg) {
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
HDC hdcMem = CreateCompatibleDC(hdc);
HBITMAP hbmOld = (HBITMAP)SelectObject(hdcMem, bitmaps[currentFileIndex]);
// Calculate scaled bitmap size maintaining aspect ratio
int scaledWidth = windowWidth;
int scaledHeight = windowHeight;
if (windowWidth > windowHeight) {
scaledWidth = windowHeight;
scaledHeight = windowHeight;
} else {
scaledWidth = windowWidth;
scaledHeight = windowWidth;
}
// Calculate position to center the bitmap
int xPos = (windowWidth - scaledWidth) / 2;
int yPos = (windowHeight - scaledHeight) / 2;
// Draw scaled bitmap
StretchBlt(hdc, xPos, yPos, scaledWidth, scaledHeight, hdcMem, 0, 0, bitmapWidth, bitmapHeight, SRCCOPY);
SelectObject(hdcMem, hbmOld);
DeleteDC(hdcMem);
EndPaint(hwnd, &ps);
} break;
case WM_SIZE: {
windowWidth = LOWORD(lParam);
windowHeight = HIWORD(lParam);
InvalidateRect(hwnd, NULL, TRUE);
} break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
// Main WinMain function
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
WNDCLASS wc = { 0 };
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszClassName = "ImageViewer";
RegisterClass(&wc);
hwnd = CreateWindow("ImageViewer", "BIN Image Viewer", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, IMAGE_WIDTH, IMAGE_HEIGHT, NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, nCmdShow);
// Load all bin files into RAM and create bitmaps
for (int i = 1; i <= MAX_FILES; i++) {
if (!loadBinFile(i, images[i - 1])) {
break;
}
bitmaps[i - 1] = createBitmapFromImage(images[i - 1]);
}
// Main loop to cycle images with 16 ms delay
while (true) {
currentFileIndex = (currentFileIndex + 1) % MAX_FILES;
InvalidateRect(hwnd, NULL, FALSE); // Use FALSE to prevent background erase
UpdateWindow(hwnd);
Sleep(16);
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT) {
return msg.wParam;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return 0;
}

(if you try this animation, you can pause the animation by clicking on the title of the window)
Binary files used:
https://drive.google.com/file/d/1wsQgdx ... sp=sharing
We're talking about my cpu staying at 0% usage... obviously there was cpu usage but it was very low. When using GDI I was using 1% of my graphics card, watching videos in 240p consumes me more(4-5%).