Интересное решение. Но к сожалению мне не пойдет, т.к. в итоге будет юзаться не мсвс на релизе и это может поебать код. А так, я даж что-то себе да приберу, ty. На счет не хватка памяти, мужик, в таких случаях там и так пол хака поебетьсе. Уж слишком много хранит хак в памяти, базару ноль, некоторые вещи я потом мб вынесу, но не сейчас. Я стараюсь memprof юзать от мсвс дабы следить где я какаю, поэтому один раз переписывал систему хранения ентити. Я вообще изначально заюзал enTT (на гите лежат сурсы), чтобы создать систему хранения ентити в хаке (projectiles, entities с кастом флагами и т.п. как пример hero, player_controller, tree, courier и т.п., чтобы в случае опр. надобности быстро дергать именно нужные ентити), в итоге увидел, что либо я напастил хуево, либо enTT как-то странно написан. Потратил день, нехуя не понял в чем моя ошибка и написал с нуля entity_manager свой. С тех пор не испытывал проблем.
На счет эксепшионов, я думал кастом класс написать, но руки никак не дойдут до этого. В хаке есть куча иных вещей которые нужно делать, а это уже как-то - как будет минутка, не столь критично, мне не западло написать чуть больше символов.
Я так понимаю у тебя модульный хак или отдельная либа в хаке которая дергает export'ы? Интересно. А так, спасибо, что-то да спизжу для себя.
(П.С. Я понимаю, что код ты написал по быстрому, но все таки лестница из скобок не очень, зачем она если можно код упростить? Оно становиться более читаемым, имхо)
std::size_t GetOffset(const std::string_view& name)
{
if (const auto& paths = split_path_into_relative_paths(name); paths.size() == 3)
{
const auto& mod = paths.at(0);
const auto& cls = paths.at(1);
const auto& var = paths.at(2);
for (const auto* scope : CSchemaSystem::GetInstance().GetTypeScopes())
{
if (!scope && scope->GetName() != mod)
continue;
for (const auto* Class : scope->GetClasses())
{
if (!Class && Class->GetName() != cls)
continue;
for (const auto& member : Class->GetMembers())
{
if (member.GetName() == var)
{
return member.GetOffset();
}
}
}
}
}
throw std::runtime_error{ std::format("CSchemaRuntimeGetter: netvar not found({})", name) };
}
да там уебанская лестница, у меня два модуля, старый(runtime с SchemaMember) и новый(runtime_rework с SchemaMember2Test) я из старого скопипастил этот говнокод для теста))
бтв
if (!scope && scope->GetName() != mod)
continue;
ты наверно имел ввиду ||, то есть если (либо nullptr либо нейм != нужному) то скип. а получилось &&, то есть (если нуллптр И нуллптр->нейм не равен нужному)
как видишь уже сразу у тебя баг получился халявный от твоего способа)) понятно что на скорую руку но тем не менее) если бы ты два условия ебанул в две строки отдельно вполне вероятно что ты бы этого бага не допустил)
эта лесенка с if(Class) if(Scope) фиксится(по уму) ваще по-другому - надо референсы итерировать а не указатели. чек и скип вынести под капот и тогда просто даже возможности тут ошибиться не будет(не будет чеков на нуллптр они под колпаком). и до кучи еще и все эти сравнения имён можно в FindScope, FindClass какой-нибудь захуярить просто(которые еще и детальные исключения будут выкидывать мол Scope или Class не найдет вместо общего "не удалось найти нетвар"). тогда и чека на имя не будет. и чека на то нашло масштаб или нет тоже не будет ибо нахуй он нужен когда можно исключение кинуть что не найден масштаб/класс(а можно еще и его поймать и rethrow'нуть с доп инфой что мол "не найден класс A пока искал нетвар A/B/C")
тем не менее я лично предпочитаю лесенки и очень очень сильно не навижу любые выражения контроля кроме return в самом конце. я стараюсь вообще никогда не писать continue break(ну break нормальная тема впринципе когда из цикла надо выйти при этом не возвращаясь из функции но такая нужда редко появляется) и тд, а также return который не в самом конце. я стараюсь такую лесенку наоборот делать в которой очень четко проявляется одна конкретная ветвь(когда все условия соблюдены). таким образом логика максимально ясная получается - при соблюдении абсолютно всех условий получается "вот это", а в любом другом случае - "вот то"(кидается исключение например). а когда условия еще и разделены максимально на разные строки(if a && b -> if a + if b), то еще чётче логика прослеживается.
так что сам стиль "лестница" имба. и в нормальном коде лестниц таких огромных и не будет. тут лестница просто потому что мне лень было делать референсы вместо указателей и всякие Find'ы.
я сначала тоже лестницу не юзал, юзал всякие continue и тд, потом как увидел какое-то ужасное говнокодище с кучей continue return и тд где тупо просто не понятно что происходит нахуй(даже если логика овер простая) и перестал.
ну кр4 мораль не в читаемости а в корректности. лучше рабочий код чем читабельный. но довольно часто эти две вещи очень сильно взаимосвязаны(лестница корректнее всяких continue и тд(потому что логика чистейшая без всяких прыжков хуйков continue и тд и следовательно сложнее ошибиться потому что все максимально чётко видно(ну по крайней мере я так считаю) и прорисовывается одна чёткая успешная ветвь. с continue ты как-будто фильтруешь, отсеиваешь ненужные(и методом исключения приходишь к нужному), пробираешься сквозь джунгли, а с лесенкой ты наоборот сразу выбираешь только нужное и не обращаешь внимания на ненужное, идёшь на свет вдалеке и тебе пофигу на темноту вокруг), но при этом она сама по себе тоже говно - нужно итерировать референсы просто вместо указателей + юзать Find'ы, вот это будет и читаемо и корректно)
я фулл модули везде юзаю хедеры для лохов)) не знаю почему пидарасы из комитета с++ решили токо к с++20 завезти нормальную систему пакаг. хедеры это мусор Сшный, куда не глянь джаваскрипт джава шарп питон везде модули/пакаги а в с++ хедеры блядь.
и ты не так понял - на кленге и гсс работает, там просто дополнительно зареференсить надо, чтобы компилятор не игнорил(ну то есть код компилится но конструктор у static inline члена не вызывается ибо компилятор решает что это мусор и оптимизирует(убирает) к хуям(потому что он не видит нигде что ты этот член используешь. поэтому и надо его "задействовать" чтобы компилятор понял что это не мусор)). ну в общем гсс и кленг с одной стороны умные а с другой туповатые(умные потому что оптимизируют а тупые потому что никто их не просил этого делать)
ну и саппорт новых фич(в частности модулей и концептов) нищий немножко в гсс и кленге
class shit
{
class xxx{}
static inline xxx cock{};//компилятор оптимизирует подумав что это мусор(если нигде нет упомянания cock)
//shit(){cock;}//а вот если референснуть переменную cock то компилятор уже не будет эту хуйню оптимизировать(я бы даже сказал ошибочно оптимизировать ибо это нихуя не оптимизация). на мсвс и так работает а на кленге и гсс нужно будет раскомментить эту строку
}
на с++ сложно накакать, как ты это делаешь? юзай RAII ни одной каки не будет и быть не может. задай всему масштаб(основной масштаб чита это его жизнь(жизнь это период от инита до деинита)) и всё. ты тупо не сможешь накакать, потому что все вещи будут зарождаться в масштабе и в нём же умирать.
любой ресурс(оперативка и тд) должен кто-то держать. когда держатель(ака менеджер) дохнет(а дохнет он при выходе из чита) - все что он держит тоже дохнет. с таким дизайном каки просто невозможны. если у тебя ресурс - держи его(желательно еще и централизованно - то есть в менеджере), не выкидывай его. не выкидываешь ресурсы = не срёшь. подыхаешь = убиваешь всех кого держишь(и они убивают всех кого они держат, и так далее)
на, накидал тебе ночного говнокода, потести внимательно, вроде нет ликов
#define _CRTDBG_MAP_ALLOC 1
#include <stdlib.h>
#include <crtdbg.h>
#include <Windows.h>
#include <iostream>
#include <vector>
#include <thread>
#include <array>
template<class T>
class Singleton
{
static inline T* instance{ nullptr };
public:
static bool HasInstance() noexcept { return instance != nullptr; }
private:
template<class... Args>
static void Create(T& real_instance, Args&&... args) noexcept(std::is_nothrow_constructible_v<T, Args...>)
{
static_assert(std::is_nothrow_constructible_v<T>, "Singleton requires -> T has public default noexcept constructor and destructor.");
if (!HasInstance())
{
T temp{ std::forward<Args>(args)... };
using std::swap;
swap(temp, real_instance);
instance = &real_instance;
}
}
public:
static void Destroy() noexcept
{
if (HasInstance())
{
GetInstance().~T();
instance = nullptr;
}
}
public:
static auto& GetInstance() noexcept
{
return *instance;
}
class Adaptor
{
union { T instance_impl_nodestruct; };
T& GetInstanceImpl() noexcept
{
return instance_impl_nodestruct;
}
public:
template<class... Args>
Adaptor(Args&&... args) noexcept(std::is_nothrow_constructible_v<T, Args...>)
{
Create(GetInstanceImpl(), std::forward<Args>(args)...);
}
Adaptor(const Adaptor&) = delete;
Adaptor& operator=(const Adaptor&) = delete;
Adaptor(Adaptor&&) noexcept = delete;
Adaptor& operator=(Adaptor&&) noexcept = delete;
~Adaptor() noexcept { Destroy(); }
operator const T& () const noexcept { return GetInstance(); }
};
public:
Singleton() = default;
protected:
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
Singleton(Singleton&&) noexcept = default;
Singleton& operator=(Singleton&&) noexcept = default;
~Singleton() = default;
};
class IResource
{
public:
virtual ~IResource() noexcept = default;
};
class resourceman
:
public Singleton<resourceman>
{
friend class Singleton<resourceman>;
class StupidCock
{
std::string name{};
bool is_valid{ false };
public:
void Destroy()
{
if (is_valid)
std::cout << "~StupidCock: " << name << std::endl;
}
public:
StupidCock() noexcept = default;
StupidCock(const std::string_view& _name) : name{ _name }, is_valid{ true } {}
StupidCock(const StupidCock&) = delete;
StupidCock& operator=(const StupidCock&) = delete;
StupidCock(StupidCock&& other) noexcept
: name{ std::move(other.name) }
{
is_valid = other.is_valid;
other.is_valid = false;
}
StupidCock& operator=(StupidCock&& other) noexcept
{
Destroy();
name = std::move(other.name);
is_valid = other.is_valid;
other.is_valid = false;
return *this;
}
~StupidCock()
{
Destroy();
}
};
private:
std::vector<std::unique_ptr<IResource>> resources{};
StupidCock stupid_cock{};
public:
resourceman() noexcept = default;
protected:
resourceman(const std::string_view& _shit) : stupid_cock{ "cock???" }
{
if (_shit == "shit")
throw std::runtime_error{"SHIT GONE DOWN!!!"};
}
resourceman(const resourceman&) = delete;
resourceman& operator=(const resourceman&) = delete;
resourceman(resourceman&& other) noexcept
:
resources{ std::move(other.resources) },
stupid_cock{ std::move(other.stupid_cock) }
{}
resourceman& operator=(resourceman&& other) noexcept
{
resources = std::move(other.resources);
stupid_cock = std::move(other.stupid_cock);
return *this;
}
public:
~resourceman() noexcept
{}
friend void swap(resourceman& a, resourceman& b) noexcept
{
auto temp_a{ std::move(a) };
a = std::move(b);
b = std::move(temp_a);
}
public:
template<class T, class... Args>
static auto& emplace(Args&&... args)
{
auto result = std::make_unique<T>(std::forward<Args>(args)...);
const auto result_ptr = result.get();
GetInstance().resources.push_back(std::move(result));
return *result_ptr;
}
};
class Shit : public IResource
{
std::string name{};
public:
Shit(const std::string_view& _name) : name{ _name } {}
Shit(const Shit&) = default;
Shit(Shit&&) noexcept = default;
~Shit()
{
std::cout << "~Shit" << std::endl;
}
std::string_view GetName() const noexcept { return name; }
};
class Entity : public IResource
{
std::string name{};
public:
Entity(const std::string_view& _name) : name{ _name } {}
Entity(const Entity&) = default;
Entity(Entity&&) noexcept = default;
~Entity()
{
std::cout << "~Entity" << std::endl;
}
std::string_view GetName() const noexcept { return name; }
};
class I_Need_To_Be_Closed : public IResource
{
using large_arr = std::array<char, (1 << 24)>;
std::unique_ptr<large_arr> shit{};
public:
I_Need_To_Be_Closed(const std::string_view& name)
: shit{ std::make_unique<large_arr>() }
{
if (name == "shit")
throw std::runtime_error{ "WOOO SHITTTT" };
}
I_Need_To_Be_Closed(const I_Need_To_Be_Closed&) = delete;
I_Need_To_Be_Closed(I_Need_To_Be_Closed&&) noexcept = default;
~I_Need_To_Be_Closed()
{
std::cout << "~I_Need_To_Be_Closed" << std::endl;
}
};
void submain()
{
resourceman::Adaptor resources
{
"shi"
//uncomment for testing VVV
//"t"
};
std::cout << "Shit -> " << resourceman::emplace<Shit>("cunt").GetName() << std::endl;
const auto lambda = []()
{
try
{
std::cout << "Entity -> " << resourceman::emplace<Entity>("cock").GetName() << std::endl;
resourceman::emplace<I_Need_To_Be_Closed>
(
"shi"
//uncomment for testing VVV
//"t"
);
}
catch (const std::exception& ex)
{
std::cout << "other_thread: " << ex.what() << std::endl;
}
};
HANDLE Xthread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)+lambda, 0, 0, 0);
if (Xthread != NULL)
{
WaitForSingleObject(Xthread, INFINITE);
CloseHandle(Xthread);
}
//std::jthread Xthread{ lambda };//performs some sort of CRT allocations under the hood so fuck it
}
void truemain()
{
_CrtMemState sOld{};
try
{
_CrtMemCheckpoint(&sOld);
submain();
}
catch (const std::exception& ex)
{
std::cout << ex.what() << std::endl;
}
_CrtMemState sNew{};
_CrtMemState sDiff{};
_CrtMemCheckpoint(&sNew);
_CrtMemDifference(&sDiff, &sOld, &sNew);
OutputDebugString(L"-----------_CrtMemDumpStatistics ---------\n");
_CrtMemDumpStatistics(&sDiff);
OutputDebugString(L"-----------_CrtMemDumpAllObjectsSince ---------\n");
_CrtMemDumpAllObjectsSince(&sOld);
OutputDebugString(L"-----------_CrtDumpMemoryLeaks ---------\n");
_CrtDumpMemoryLeaks();
}
int main()
{
printf("one-time 4k bytes CRT allocation <----(caused by printf)...\n");
truemain();
return 0;
}