C
C#2y ago
Timinator

❔ PInvoke, Reflection or winapi for loading a DLL?

I need some advise for how to implement a dynamically loadable dll that ive written in c++ and exported with extern C function wrappers. Unfortunally im not that proefficient in C#. i cant seem to find a dynamic way to make calls via those function pointers.
110 Replies
Timinator
TiminatorOP2y ago
for reference this is my importer so far
Timinator
TiminatorOP2y ago
and incase that its relevant this is my exporter from within the dll project
Timinator
TiminatorOP2y ago
additionally, this is my c++ sample of my working importer
ero
ero2y ago
i don't quite understand. what's with all the loadlibrary stuff? why not just dllimport the dll directly?
Timinator
TiminatorOP2y ago
because i want to be able to unload it at runtime if the implementation utilizing the lib wishes to do so unlike the kernel32.dll the iniparser.dll can be replaced or deleted at runtime but only if the reference count is 0 additionally the behavior could not be updated at runtime
ero
ero2y ago
why make it so difficult for yourself... i don't get it...
Timinator
TiminatorOP2y ago
if using reflection im writing a game in unity and i want my backend to be updateable without shutting the server down to make it short xD additionally i aim for similar behavior across the languages and id prefer to refrain from using unsafe scopes because its from what ive seen not favored in the c# community.
ero
ero2y ago
yeah i bet #allow-unsafe-blocks would love to hear about that completely bogus take with those c++ definitions you can't avoid unsafe code anyway because you chose to take pointers
Timinator
TiminatorOP2y ago
well it would work with reflection i mean it does
ero
ero2y ago
why write the parser in c++ at all? so many questions
Timinator
TiminatorOP2y ago
to load it in c, c++, c#, java, python etc from one binary without rewriting the entire definitions in every language with the proper setup it can be compiled under windows and linux to generate the different binary formats and then be loaded by each language using their own importer also the intptr returned by GetProcAddress is returning valid pointers already. i literally just need to be able to make calls to it
ero
ero2y ago
someone else will have to help you there. this is quite frankly a really bad setup and you should consider just dllimporting the library directly
Timinator
TiminatorOP2y ago
isee
Accord
Accord2y ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.
Timinator
TiminatorOP2y ago
anyone?
sibber
sibber2y ago
the reflection method is for managed assemblies what you probably want is LoadLibrary from win32 also why do you have pinvoke and winapi as two separate things?
Accord
Accord2y ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.
Timinator
TiminatorOP2y ago
im using LoadLibraryA yes id love the second approach with function pointers but im not sure if having users enable the unsafe context for their project just to use my api is beginner friendly for the name might scare them or similar things maybe im overthinking that though xD id be optimal for across all the languages to just have the user include the header(importer) (or pseudo header like in c#), initialize the dll/so/lib/a etc library and ready to go.
sibber
sibber2y ago
if you want user frendliness write a wrapper in c# if they know they have to pinvoke then they know they have to do unsafe stuff why the ansi version
Timinator
TiminatorOP2y ago
so a local unsafe scope wont require setting fiddling? okay to be fair i presumed that i had to change the attribute of the wrapper class itself if that works id be perfect, or did i missunderstand your answer?(cant test it as of rn) because for now ive written the entire lib using chars its really just a matter of copy paste to refactoring to / adding the utf format
Accord
Accord2y ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity. Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.
Timinator
TiminatorOP2y ago
may it be assumed that im using the function pointer correctly here?
Timinator
TiminatorOP2y ago
with the constructors resulting in this
public IniParser()
{
IniParser.Initialize("./");

if (IniParser.m_pIniParser_new != nullptr)
{
unsafe
{
this.m_pIniParser = (IntPtr)(delegate* unmanaged[Stdcall]<IntPtr>)IniParser.m_pIniParser_new.ToPointer();
}
}
}

public IniParser(ref INIPARSER_MSG pMsg)
{
IniParser.Initialize("./");

if (IniParser.m_pIniParser_new != nullptr)
{
unsafe
{
this.m_pIniParser = (IntPtr)(delegate* unmanaged[Stdcall]<IntPtr>)IniParser.m_pIniParser_new.ToPointer();
}

if (this.m_pIniParser != nullptr)
{
pMsg.m_msg = INIPARSER_OK;
}
else
{
pMsg.m_msg = INIPARSER_ERROR;
}
}
}
public IniParser()
{
IniParser.Initialize("./");

if (IniParser.m_pIniParser_new != nullptr)
{
unsafe
{
this.m_pIniParser = (IntPtr)(delegate* unmanaged[Stdcall]<IntPtr>)IniParser.m_pIniParser_new.ToPointer();
}
}
}

public IniParser(ref INIPARSER_MSG pMsg)
{
IniParser.Initialize("./");

if (IniParser.m_pIniParser_new != nullptr)
{
unsafe
{
this.m_pIniParser = (IntPtr)(delegate* unmanaged[Stdcall]<IntPtr>)IniParser.m_pIniParser_new.ToPointer();
}

if (this.m_pIniParser != nullptr)
{
pMsg.m_msg = INIPARSER_OK;
}
else
{
pMsg.m_msg = INIPARSER_ERROR;
}
}
}
Jester
Jester2y ago
there is no reason in casting it to a function pointer if youre then casting it to an intptr
Timinator
TiminatorOP2y ago
how else would i make a call to a function by a function pointer stored within an IntPtr
sibber
sibber2y ago
you didnt call it you just casted it
Jester
Jester2y ago
yeah thats what im thinking
Timinator
TiminatorOP2y ago
hm sec
Jester
Jester2y ago
((delegate* unmanaged[Stdcall]<IntPtr>)IniParser.m_pIniParser_new.ToPointer())();
((delegate* unmanaged[Stdcall]<IntPtr>)IniParser.m_pIniParser_new.ToPointer())();
to call it
Timinator
TiminatorOP2y ago
ah right ty makes sense xD
this->m_pIniParser = ((void* (*)())IniParser::m_pIniParser_new)();
this->m_pIniParser = ((void* (*)())IniParser::m_pIniParser_new)();
given i basically call it the same in c
Jester
Jester2y ago
yeah its pretty much the same
Timinator
TiminatorOP2y ago
right thanks alot 😄
Timinator
TiminatorOP2y ago
@Jester 😛
bool dllLoad = IniParser.Initialize("./");

Console.WriteLine("Loading DLL: " + dllLoad);

INIPARSER_MSG msg = new INIPARSER_MSG();

IniParser parser = new IniParser(ref msg);

if (msg.m_msg == IniParser.INIPARSER_OK)
{
Console.WriteLine(parser.getLineSeperator());
}
else
{
Console.WriteLine("Failed to alloc IniParser");
}
bool dllLoad = IniParser.Initialize("./");

Console.WriteLine("Loading DLL: " + dllLoad);

INIPARSER_MSG msg = new INIPARSER_MSG();

IniParser parser = new IniParser(ref msg);

if (msg.m_msg == IniParser.INIPARSER_OK)
{
Console.WriteLine(parser.getLineSeperator());
}
else
{
Console.WriteLine("Failed to alloc IniParser");
}
with this
public char getLineSeperator()
{
if (IniParser.m_pIniParser_getLineSeperator != nullptr)
{
unsafe
{
return ((delegate* unmanaged[Stdcall]<IntPtr, char>)IniParser.m_pIniParser_getLineSeperator.ToPointer())(this.m_pIniParser);
}
}

return (char)0;
}
public char getLineSeperator()
{
if (IniParser.m_pIniParser_getLineSeperator != nullptr)
{
unsafe
{
return ((delegate* unmanaged[Stdcall]<IntPtr, char>)IniParser.m_pIniParser_getLineSeperator.ToPointer())(this.m_pIniParser);
}
}

return (char)0;
}
works just fine now
Timinator
TiminatorOP2y ago
the line seperator is supposed to be the comma so it works
Jester
Jester2y ago
good
Timinator
TiminatorOP2y ago
just one further question, do i need to delete Marshalled strings? regarding memory leaks
public string getFileExtension()
{
if (IniParser.m_pIniParser_getFileExtension != nullptr)
{
unsafe
{
IntPtr ptr = ((delegate* unmanaged[Stdcall]<IntPtr, IntPtr>)IniParser.m_pIniParser_getFileExtension.ToPointer())(this.m_pIniParser);

if (ptr != nullptr)
{
Console.WriteLine(Marshal.PtrToStringAnsi(ptr));
}
}
}

return "";
}
public string getFileExtension()
{
if (IniParser.m_pIniParser_getFileExtension != nullptr)
{
unsafe
{
IntPtr ptr = ((delegate* unmanaged[Stdcall]<IntPtr, IntPtr>)IniParser.m_pIniParser_getFileExtension.ToPointer())(this.m_pIniParser);

if (ptr != nullptr)
{
Console.WriteLine(Marshal.PtrToStringAnsi(ptr));
}
}
}

return "";
}
sibber
sibber2y ago
you need to delete ptr
Timinator
TiminatorOP2y ago
ah so i need to delete all of them in every function
sibber
sibber2y ago
you need to delete everything thats unmanaged, when youre done with it of course
Timinator
TiminatorOP2y ago
i presume the same then applies to this char?
public char getLineSeperator()
{
if (IniParser.m_pIniParser_getLineSeperator != nullptr)
{
unsafe
{
return ((delegate* unmanaged[Stdcall]<IntPtr, char>)IniParser.m_pIniParser_getLineSeperator.ToPointer())(this.m_pIniParser);
}
}

return (char)0;
}
public char getLineSeperator()
{
if (IniParser.m_pIniParser_getLineSeperator != nullptr)
{
unsafe
{
return ((delegate* unmanaged[Stdcall]<IntPtr, char>)IniParser.m_pIniParser_getLineSeperator.ToPointer())(this.m_pIniParser);
}
}

return (char)0;
}
and the solution is to make a managed copy delete the unmanaged one and return the managed one?
sibber
sibber2y ago
no this isnt a pointer you only need to delete objects that were are allocated on the heap
Timinator
TiminatorOP2y ago
the getFileExtension returns a pointer pointing to a const char* tho
sibber
sibber2y ago
from unmanaged code yes, thats an array on the heap (i assume)
Timinator
TiminatorOP2y ago
on the stack the const char* itself is on the stack
sibber
sibber2y ago
the array is on the stack?
Timinator
TiminatorOP2y ago
ya wait
sibber
sibber2y ago
if its on the stack then its deleted when the function returns so the pointer you return is invalid
Timinator
TiminatorOP2y ago
this is how my lib manages it
std::string const& IniParser::getFileExtension()
{
return this->m_settings.m_extension;
}
std::string const& IniParser::getFileExtension()
{
return this->m_settings.m_extension;
}
this is the string class object and then its actually returned by this
C_EXPORTER const char* C_IniParser_getFileExtension(IniParser* pIniParser) { return pIniParser->getFileExtension().c_str(); }
C_EXPORTER const char* C_IniParser_getFileExtension(IniParser* pIniParser) { return pIniParser->getFileExtension().c_str(); }
and this const char* is what the IntPtr in
IntPtr ptr = ((delegate* unmanaged[Stdcall]<IntPtr, IntPtr>)IniParser.m_pIniParser_getFileExtension.ToPointer())(this.m_pIniParser);
IntPtr ptr = ((delegate* unmanaged[Stdcall]<IntPtr, IntPtr>)IniParser.m_pIniParser_getFileExtension.ToPointer())(this.m_pIniParser);
points to which is then to be marshalled to a C# string so
__declspec(dllexport) const char* C_IniParser_getFileExtension(IniParser* pIniParser) { return pIniParser->getFileExtension().c_str(); }
__declspec(dllexport) const char* C_IniParser_getFileExtension(IniParser* pIniParser) { return pIniParser->getFileExtension().c_str(); }
is what the C# importer actually interfaces with so the returned value is on the stack while the actual value is on the heap within a std::string class thats encapsuled
sibber
sibber2y ago
so the string is on the heap
Timinator
TiminatorOP2y ago
yea but its encapsuled and managed by the .dll itself it only exposes a copy on the stack to the calling language
sibber
sibber2y ago
std::string frees the heap object when its destructed but it looks like its a member of a class
Timinator
TiminatorOP2y ago
it is
sibber
sibber2y ago
so it will get freed automatically when that class is destructed
Timinator
TiminatorOP2y ago
yea and i destruct the class like this
C_EXPORTER IniParser* C_IniParser_new() { return new IniParser(); };
C_EXPORTER void C_IniParser_delete(IniParser* pIniParser) { delete pIniParser; }
C_EXPORTER IniParser* C_IniParser_new() { return new IniParser(); };
C_EXPORTER void C_IniParser_delete(IniParser* pIniParser) { delete pIniParser; }
which is called by
public IniParser()
{
IniParser.Initialize("./");

if (IniParser.m_pIniParser_new != nullptr)
{
unsafe
{
this.m_pIniParser = ((delegate* unmanaged[Stdcall]<IntPtr>)IniParser.m_pIniParser_new.ToPointer())();
}
}
}

public IniParser(ref INIPARSER_MSG pMsg)
{
IniParser.Initialize("./");

if (IniParser.m_pIniParser_new != nullptr)
{
unsafe
{
this.m_pIniParser = ((delegate* unmanaged[Stdcall]<IntPtr>)IniParser.m_pIniParser_new.ToPointer())();
}

if (this.m_pIniParser != nullptr)
{
pMsg.m_msg = INIPARSER_OK;
}
else
{
pMsg.m_msg = INIPARSER_ERROR;
}
}
}

~IniParser()
{
if (IniParser.m_pIniParser_delete != nullptr)
{
unsafe
{
((delegate* unmanaged[Stdcall]<IntPtr, void>)IniParser.m_pIniParser_delete.ToPointer())(this.m_pIniParser);
}
}
}
public IniParser()
{
IniParser.Initialize("./");

if (IniParser.m_pIniParser_new != nullptr)
{
unsafe
{
this.m_pIniParser = ((delegate* unmanaged[Stdcall]<IntPtr>)IniParser.m_pIniParser_new.ToPointer())();
}
}
}

public IniParser(ref INIPARSER_MSG pMsg)
{
IniParser.Initialize("./");

if (IniParser.m_pIniParser_new != nullptr)
{
unsafe
{
this.m_pIniParser = ((delegate* unmanaged[Stdcall]<IntPtr>)IniParser.m_pIniParser_new.ToPointer())();
}

if (this.m_pIniParser != nullptr)
{
pMsg.m_msg = INIPARSER_OK;
}
else
{
pMsg.m_msg = INIPARSER_ERROR;
}
}
}

~IniParser()
{
if (IniParser.m_pIniParser_delete != nullptr)
{
unsafe
{
((delegate* unmanaged[Stdcall]<IntPtr, void>)IniParser.m_pIniParser_delete.ToPointer())(this.m_pIniParser);
}
}
}
those
sibber
sibber2y ago
yeah so you dont need to delete anything here, since its just returning a member of a class
Timinator
TiminatorOP2y ago
so i wonder if i actually need to delete the ptr in
public string getFileExtension()
{
if (IniParser.m_pIniParser_getFileExtension != nullptr)
{
unsafe
{
IntPtr ptr = ((delegate* unmanaged[Stdcall]<IntPtr, IntPtr>)IniParser.m_pIniParser_getFileExtension.ToPointer())(this.m_pIniParser);

if (ptr != nullptr)
{
string foo = Marshal.PtrToStringAnsi(ptr);

if (foo != null)
{
Console.WriteLine(foo);
}
}
}
}

return "";
}
public string getFileExtension()
{
if (IniParser.m_pIniParser_getFileExtension != nullptr)
{
unsafe
{
IntPtr ptr = ((delegate* unmanaged[Stdcall]<IntPtr, IntPtr>)IniParser.m_pIniParser_getFileExtension.ToPointer())(this.m_pIniParser);

if (ptr != nullptr)
{
string foo = Marshal.PtrToStringAnsi(ptr);

if (foo != null)
{
Console.WriteLine(foo);
}
}
}
}

return "";
}
sibber
sibber2y ago
this should implement IDisposable
Timinator
TiminatorOP2y ago
hm but in general, i dont need to delete anything here other than the m_pIniParser ptr ofc?
sibber
sibber2y ago
you dont need to delete that
Timinator
TiminatorOP2y ago
from how i understand it now the delegate doesnt allocates new memory on the heap here
sibber
sibber2y ago
the std::string handles it when its destructed
Timinator
TiminatorOP2y ago
kk
sibber
sibber2y ago
yup
Timinator
TiminatorOP2y ago
yea i tried to write the actual library impelentation in a way that it manages itself so other higher languages dont need to other than the actual IniParser object from the lib
sibber
sibber2y ago
yup thats good id also make this a property
Timinator
TiminatorOP2y ago
ill read into properties xD im not that versed in c#
sibber
sibber2y ago
in c# we use properties instead of get and set methods
Timinator
TiminatorOP2y ago
hmm checked microsofts documentation, seems the properties are getters and setters allowing for a more "natural" access to the values?
public class TimePeriod
{
private double _seconds;

public double Hours
{
get { return _seconds / 3600; }
set
{
if (value < 0 || value > 24)
throw new ArgumentOutOfRangeException(nameof(value),
"The valid range is between 0 and 24.");

_seconds = value * 3600;
}
}
}
public class TimePeriod
{
private double _seconds;

public double Hours
{
get { return _seconds / 3600; }
set
{
if (value < 0 || value > 24)
throw new ArgumentOutOfRangeException(nameof(value),
"The valid range is between 0 and 24.");

_seconds = value * 3600;
}
}
}
just copied this from the msdocs
sibber
sibber2y ago
yup you access them like fields but instead they call get/set/init
Timinator
TiminatorOP2y ago
well if thats the way to go ill write the c# importer like that, always good to implement language specific features
sibber
sibber2y ago
yup
Timinator
TiminatorOP2y ago
like C developers would tear my head off for an OOP approach xD
sibber
sibber2y ago
btw we have expression bodies now so get => _seconds / 3600;
Timinator
TiminatorOP2y ago
i think ive seen that in c++ this syntax
sibber
sibber2y ago
really?
Timinator
TiminatorOP2y ago
which is a rather new feature yea xD
sibber
sibber2y ago
i thought cpp didnt have that
Timinator
TiminatorOP2y ago
c++20 or so its adopting alot of high level language features as of lately
sibber
sibber2y ago
i cant keep up with c++
Timinator
TiminatorOP2y ago
found this
#include <ostream>
#include <iostream>

class A {
public:
A(int nr) : number(nr) { }
~A() { }
int getNr() const { return this->number; }
int number;
};

std::ostream& operator<<(std::ostream& os, const A& a) {
os << a.getNr();
return os;
}
#include <ostream>
#include <iostream>

class A {
public:
A(int nr) : number(nr) { }
~A() { }
int getNr() const { return this->number; }
int number;
};

std::ostream& operator<<(std::ostream& os, const A& a) {
os << a.getNr();
return os;
}
and then they can be called like this
int main()
{
A a(42);
std::cout << a << std::endl; // prints 42

return 0;
}
int main()
{
A a(42);
std::cout << a << std::endl; // prints 42

return 0;
}
found it online really quick xD though id need to dig myself deeper into that to actually utilize it
phaseshift
phaseshift2y ago
I think you misunderstood what 'expression bodied function' in c# is. Ie in some circumstances defining a method body without {}. I don't think that's valid anywhere in c++
Timinator
TiminatorOP2y ago
to be fair i just copied that quickly since i didnt use it myself for some time now esp since im mostly forced to write in c for my uni projects. rn im experimenting with properties in c# like this?
public char lineSeperator
{
get
{
if (IniParser.m_pIniParser_getLineSeperator != nullptr)
{
unsafe
{
return ((delegate* unmanaged[Stdcall]<IntPtr, char>)IniParser.m_pIniParser_getLineSeperator.ToPointer())(this.m_pIniParser);
}
}

return (char)0;
}
set
{
if (IniParser.m_pIniParser_setLineSeperator != nullptr)
{
unsafe
{
((delegate* unmanaged[Stdcall]<IntPtr, char, void>)IniParser.m_pIniParser_setLineSeperator.ToPointer())(this.m_pIniParser, value);
}
}
}
}
public char lineSeperator
{
get
{
if (IniParser.m_pIniParser_getLineSeperator != nullptr)
{
unsafe
{
return ((delegate* unmanaged[Stdcall]<IntPtr, char>)IniParser.m_pIniParser_getLineSeperator.ToPointer())(this.m_pIniParser);
}
}

return (char)0;
}
set
{
if (IniParser.m_pIniParser_setLineSeperator != nullptr)
{
unsafe
{
((delegate* unmanaged[Stdcall]<IntPtr, char, void>)IniParser.m_pIniParser_setLineSeperator.ToPointer())(this.m_pIniParser, value);
}
}
}
}
sibber
sibber2y ago
yup you might aswell declare the whole class as unsafe
Timinator
TiminatorOP2y ago
though how do i manage multiple parameters for a setter?
sibber
sibber2y ago
you don't you use a method
Timinator
TiminatorOP2y ago
hm then ill probably use getter setter methods to keep it uniform since i have to implement those as well
bool insertLineAt(std::string key, std::vector<std::string> values, int index, INIPARSER_MSG* pMsg = nullptr)
{
if (IniParser::m_pIniParser_insertLineAt)
{
const char** ppArray = new const char* [values.size()];

if (ppArray)
{
int bytes = 0;

for (int i = 0; i < values.size(); i++)
{
#pragma warning(suppress:6386) //Falsely warning here, there is no buffer overrun in ppArray[i]
ppArray[i] = values.at(i).c_str();
bytes += sizeof(values.at(i).c_str()) + ((values.at(i).back() != '\0') ? 1 : 0);
}

bool b = ((bool (*)(void*, const char*, const char**, int, int, int))IniParser::m_pIniParser_insertLineAt)(this->m_pIniParser, key.c_str(), ppArray, bytes, sizeof(ppArray), index);

delete[](ppArray);

if (pMsg) pMsg->m_msg = INIPARSER_OK;
return b;
}
else
{
if (pMsg) pMsg->m_msg = INIPARSER_ERROR;
return false;
}
}

if (pMsg) pMsg->m_msg = INIPARSER_OK;
return false;
}
bool insertLineAt(std::string key, std::vector<std::string> values, int index, INIPARSER_MSG* pMsg = nullptr)
{
if (IniParser::m_pIniParser_insertLineAt)
{
const char** ppArray = new const char* [values.size()];

if (ppArray)
{
int bytes = 0;

for (int i = 0; i < values.size(); i++)
{
#pragma warning(suppress:6386) //Falsely warning here, there is no buffer overrun in ppArray[i]
ppArray[i] = values.at(i).c_str();
bytes += sizeof(values.at(i).c_str()) + ((values.at(i).back() != '\0') ? 1 : 0);
}

bool b = ((bool (*)(void*, const char*, const char**, int, int, int))IniParser::m_pIniParser_insertLineAt)(this->m_pIniParser, key.c_str(), ppArray, bytes, sizeof(ppArray), index);

delete[](ppArray);

if (pMsg) pMsg->m_msg = INIPARSER_OK;
return b;
}
else
{
if (pMsg) pMsg->m_msg = INIPARSER_ERROR;
return false;
}
}

if (pMsg) pMsg->m_msg = INIPARSER_OK;
return false;
}
sibber
sibber2y ago
use properties for things they make sense to be properties like this one
Timinator
TiminatorOP2y ago
and ik suppressing a warning isnt the best idea but i tested throughly to figure that the IDE throws a false warning hm so using a combination of both?
sibber
sibber2y ago
yes i doubt that
Timinator
TiminatorOP2y ago
it complained just the same with a char** allocated with malloc this way xD it doesnt like me using values.size() to index the size somehow if i use a primitive int and iterate trough the vector til the end and increment the index and then use this index theres no problem whatsoever
phaseshift
phaseshift2y ago
Why pass the vector by value? Why not use range based for loop? Is ppArray too big to go on stack? You literally allocate ppArray the line before checking if not null. Why?
Odex
Odex2y ago
i have so many questions uh, is it possible to load C++ assemblies into Java, Python and such languages? I didn't know that was a thing
phaseshift
phaseshift2y ago
They get loaded into memory. Yes python can run cpp DLLs via a C ffi layer. That's what python c extensions are, and why compiling/installing on Windows is an annoyance
Odex
Odex2y ago
I still have no clue what you're trying to do with your parser, why complicate your life so much? If you want to share the same results across multiple languages, why don't you build a standalone parser which writes your desired data into a json or such files
Timinator
TiminatorOP2y ago
1 sec i can change it to reference, a range based for loop requires more time than using vector.size() to get the size, ppArray is not too big but i want it of dynamic size, i check if its not null because heap allocations can throw bad allocs and then everything goes boom if i have a bad alloc yes
sibber
sibber2y ago
heap alocations can throw bad allocs
what does this mean?
phaseshift
phaseshift2y ago
In order, Highly doubtful, use std::array, if it throws bad alloc then you don't get to the next line so no need to check
Timinator
TiminatorOP2y ago
well if you attempt to allocate memory on the heap and the underlying OS and to that extend kernel is not able to allocate the memory for you because, not enough ram, not enough consequtive free memory in ram, no or insufficient page file, corrupted hardware(RAM) etc etc then it returns a null pointer which tells u that the OS was not able to allocate memory for you in which case, you have undefined behavior
phaseshift
phaseshift2y ago
new can throw if there is insufficient memory
sibber
sibber2y ago
so the solution is to always null check whenever you allocate something? that sounds terrible
Timinator
TiminatorOP2y ago
well what is the alternative even std::vector can throw a bad alloc xD the alternative is to hope that nothing breaks tbh if u dont play with ur OS settings and dont install faulty ram, most likely nothing ever happens but better safe than sorry
phaseshift
phaseshift2y ago
What you're doing doesn't help anything tho
Timinator
TiminatorOP2y ago
how so
sibber
sibber2y ago
i mean if theres insufficient memory should your app continue running anyway?
Timinator
TiminatorOP2y ago
up to the programmer whos using the lib to decide
phaseshift
phaseshift2y ago
Exceptions unwind the stack
Timinator
TiminatorOP2y ago
its not really hard to change either implementation anyways
Accord
Accord2y ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.
Want results from more Discord servers?
Add your server