diff --git a/CMakeLists.txt b/CMakeLists.txt index 4f07de3..b0b80e1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,7 +51,7 @@ macro(blt_add_project name source type) project(tower-defense) endmacro() -project(tower-defense VERSION 0.0.3) +project(tower-defense VERSION 0.0.4) option(ENABLE_ADDRSAN "Enable the address sanitizer" OFF) option(ENABLE_UBSAN "Enable the ub sanitizer" OFF) diff --git a/lib/blt-with-graphics b/lib/blt-with-graphics index f49ee27..479247a 160000 --- a/lib/blt-with-graphics +++ b/lib/blt-with-graphics @@ -1 +1 @@ -Subproject commit f49ee2766ae82aa052973f977c1556d2c734c70f +Subproject commit 479247a5a3d3c3e7521eac21e41bea3fd3b92281 diff --git a/src/main.cpp b/src/main.cpp index 58bdd68..c7bed4f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,6 +4,8 @@ #include "blt/gfx/renderer/camera.h" #include "blt/gfx/renderer/resource_manager.h" +void michael_examples(); + blt::gfx::matrix_state_manager global_matrices; blt::gfx::resource_manager resources; blt::gfx::batch_renderer_2d renderer_2d(resources, global_matrices); @@ -13,6 +15,7 @@ void init(const blt::gfx::window_data&) { using namespace blt::gfx; + BLT_INFO("This should be colored lol"); global_matrices.create_internals(); resources.load_resources(); @@ -40,5 +43,7 @@ void destroy(const blt::gfx::window_data&) int main() { + michael_examples(); + blt::gfx::init(blt::gfx::window_data{"My Sexy Window", init, update, destroy}.setSyncInterval(1)); } \ No newline at end of file diff --git a/src/michael_examples.cpp b/src/michael_examples.cpp new file mode 100644 index 0000000..ec4076d --- /dev/null +++ b/src/michael_examples.cpp @@ -0,0 +1,164 @@ +/* + * + * Copyright (C) 2025 Brett Terpstra + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +// this is the dynamic array (ArrayList<> from Java) of C++ +// Any time you think you might need new/delete, especially new[]/delete[], you probably want this. +#include + +// usually this is the only logging header you need to manually bring in. status.h is also a good one +#include +// this is the base iterator header and includes all others. If you only need one type of iterator I would suggest manually importing those ones +// over brining in the dependency on all of them. This way compile times are lower +#include + +// When using hashmaps, please use blt's internal hashmap file. This will attempt to use the most optimal hashmap. +// If the parallel-hashmaps submodule isn't included it will use the std::unordered_map +// this brings in blt::hashset_t and blt::hashmap +#include + +// this is also a very useful header. it defines rust style fixed size integer types. blt::i32 +#include + +#include + +void michael_examples() +{ + /* + * Let's start with some basic modern C++. + * You know from 2P95 that copy constructors are a thing, right? + */ + struct rule_of_3_t // as a side note, please denote classes with _t, this way when defined a variable you can do test1_t test1; without error. + { + public: + // Earl never taught this, but this is called the rule of 0/3/5 + rule_of_3_t() = default; + + // if you have a copy constructor, copy assignment, or destructor defined, then you must have the other two as well. + rule_of_3_t(const rule_of_3_t& copy) + { + // do copying + } + + rule_of_3_t& operator=(const rule_of_3_t& copy) + { + // always check for self assignment + if (this == ©) + return *this; + // do copying but where it's assignment to an exiting valid object + // Note: in this case you may have to cleanup old resources. + return *this; + } + + // In this case we now have completed the rule of 3. + // you can use = default to make the compiler generate the default for this class or = delete; to disallow the usage of that function + // copy constructor -> you can't copy the object + // copy assignment -> you can't copy the object but only when assigning to another object (this is sometimes useful) + // destructor -> can't delete the object + ~rule_of_3_t() = delete; + }; + + // quickly on move semantics. + struct rule_of_5_t + { + // member initializer list constructor. Please use this style to init class variables + // the initializer is run before any other code, thus doing it inline guarantees the class is in a valid state. + explicit rule_of_5_t(const blt::i32 id): m_id(id) + { + BLT_ASSERT(m_id > 0 && "id must be greater than 0 (zero is used to represent empty state)"); + BLT_TRACE("I ({}) was constructed", m_id); + } + + // when using either std::trivially_copyable_v types or types with a copy constructor defined, + // clion will warn that a default constructor can be used. + // This is because the compiler can automatically generate a default constructor for you. + // We define one here for education + rule_of_5_t(const rule_of_5_t& copy): m_id(copy.m_id) + { + BLT_TRACE("I ({}) was copied constructed", m_id); + } + + // moving an object is not required to make the old object invalid, but it is a good idea to do so. + // if m_id was a pointer, it would need to be set to null, as the destructor is run on moved-from objects + // meaning you would get a double free error + rule_of_5_t(rule_of_5_t&& move) noexcept: m_id(std::exchange(move.m_id, 0)) + { + BLT_TRACE("I ({}) was moved constructed", m_id); + } + + rule_of_5_t& operator=(const rule_of_5_t& copy) + { + // always check for self assignment + if (this == ©) + return *this; + m_id = copy.m_id; + BLT_TRACE("I ({}) was copied assigned", m_id); + return *this; + } + + rule_of_5_t& operator=(rule_of_5_t&& move) noexcept + { + // always check for self assignment + if (this == &move) + return *this; + m_id = std::exchange(move.m_id, 0); + BLT_TRACE("I ({}) was moved assigned", m_id); + return *this; + } + + ~rule_of_5_t() + { + // take a look inside blt tests for examples on how to use the logging library + BLT_TRACE("I ({}) am deleted", m_id); + } + + private: + // member (private) variables should be prefixed with m_ + blt::i32 m_id = 0; + }; + + // look at the output from this, you will understand what is going on. + { + // scoped block means lifetimes of objects only exist for the lifetime of the block + rule_of_5_t test1(1); // constructed + rule_of_5_t test2(2); // constructed + + std::vector vector_of_rules; // vector is empty + vector_of_rules.emplace_back(test1); // vector expands internally to size 1, object is copy constructed + vector_of_rules.emplace_back(std::move(test2)); // vector needs to expand, this is moved, then (1) is also moved (that sounds suboptimal right?) + + // so test1, test2, and vector_of_rules will be destroyed here. + + // (hint) order of deletion + // I (0) am deleted -> (1) that was moved when the vector expanded + // I (1) am deleted -> (1) that is in the vector + // I (2) am deleted -> (2) that is in the vector + // I (0) am deleted -> test2 object + // I (1) am deleted -> test1 object + // you'll notice that deletion is in reverse order of declaration + } + + /* + * You know from 2P95 that we can use new/delete to allocate memory. + * Don't do that. Fun fact: you basically never need to use new/delete anymore + * If you need a dynamic array (new[]/delete[]) then you can use a vector instead. + * Vectors automatically have copy/move semantics defined for elements inside, + * so when you use them inside a class def, that class automatically has its move and copy constructors filled out. + * What this means is, using standard headers, you never need to worry about manual memory management. + * This is called the rule of 0. You don't need to define copy/move/destructors when the underlying types handle it for you. + */ +}