Jump to content

Unimportant

Member
  • Posts

    1,230
  • Joined

  • Last visited

Everything posted by Unimportant

  1. They were highly competitive, just often not given a fair chance. A computer is much more then just a CPU, especially in those days where the choice of motherboard still made a huge difference. Many a Cyrix CPU, being the cheaper choice, found itself on a motherboard with a crappy chipset and no cache (the COAST module was the first to go when cutting corners). Given equal platforms they gave Intel a run for their money. Most reviews are gone so it's hard to find good evidence. Managed to find this for what it's worth: https://www.fermimn.edu.it/inform/materiali/evarchi/cyrix.dir/perfchrt.htm Then came Quake, which exploited the Pentium quirk mentioned above to run floating point and integer in parallel, making the Pentium fly in quake and giving the Cyrix a reputation of "poor floating point performance". Which was not true at all, their floating point was fine, it just could not run in parallel with integer instructions.
  2. And succeeded! Their 6x86 was competitive and cheaper. They were undeservedly killed off because they got unlucky with some of Carmack's and Abrash's magic in quake. (carefully hand-crafting code to run floating point and integer instructions in parallel by taking advantage of a quirck in the pentium).
  3. It's not, I've seen memtest86 give known faulty memory a pass many, many times. Use Memtest for windows: https://hcidesign.com/memtest/ While no software memory testing utility is perfect, it detects way more issues then memtest86.
  4. You can but you'll have to do all the setup yourself and it's platform dependant. Here's the documentation for MSVC https://docs.microsoft.com/en-us/cpp/build/reference/nodefaultlib-ignore-libraries?view=vs-2019
  5. Yes, they are provided by the runtime library (often delegated to malloc under the hood). Most platforms combine the runtime and standard library into one. If you compile without it you lose the bootstrapping and proper exit code along with the standard library functions. The STL is a template library and because the compiler needs to be able to see the full implementation of templates at the point they're invoked, the headers ARE the full implementation, there should be no separate binaries for the STL.
  6. The STL is header only, perhaps you mean the runtime libraries?
  7. This isn't required (and actually won't compile). You're creating a automatic variable on the stack, not allocating dynamically. Its destructor will be called automatically when it goes out of scope and stack memory is managed automatically. Only dynamically allocated memory needs to be freed explicitly, but smart pointers and containers take care of that for you. Calling the constructor manually on a already constructed instance should fail to compile. Calling the destructor manually is allowed for special reasons but in general it's wrong as the destructor would be called again automatically when the object goes out of scope and destructing twice is undefined behavior. When referring to another object owned elsewhere, references are preferred because of their syntax (references don't require special syntax as opposed to pointers). However, there are certain situations where you don't have a choice: Pointers can be nullptr, references can't be null. Sometimes you want nullptr to be a possibility, saying "currently not pointing to anything". Sometimes you want to be sure something is always pointing to something valid, in that case use a reference. Pointers can be repointed to point to something else, references can't be reseated. A side effect of this is the implicit deletion of assignment operators on objects which have a reference member. For this reason you'll often be forced to use pointers as classmembers.
  8. No, in C++ a struct and a class are the same, except for the fact that struct members and inheritance are public by default whereas they are private by default for class. Correct. As per the given example you can include the header of the forward declared class in the CPP file and then you can use it completely. The dumb pointer simply refers to a object that is owned elsewhere, so it can be accessed, but the owner is responsible for it's deletion. In the given smart pointer example the memory is owned by std::unique_ptr pThing in function SomeFunction. A dumb pointer to the managed object is passed to function DoSomething so that DoSomething can access the object, but the ownership and responsibility remains with pThing in SomeFunction. If DoSomething throws then the stack will be unwound until someone tries to catch the exception. It's not being caught in DoSomething so that function will exit. It's not being caught in SomeFunction either, so that function will exit as well, but because pThing is a automatic variable in SomeFunction it will go out of scope once that function exits and it's destructor will be called, which will delete the managed memory. You can't even do that. If DoSomething throws the stack will be unwound until the exception is caught. SomeFunction will exit immediately and execution will resume at some lower level where the exception is caught. A default constructed std::unique_ptr is empty. You can manually release it and it has a overload for operator bool to check if it's currently managing something: std::unique_ptr<Thing> pThing; //default constructed unique_ptr contains nullptr by default if (pThing) //operator bool { //will not execute, pThing contains nullptr } pThing.reset(new Thing); //Create new Thing and have pThing manage it (don't do this, use std::make_unique) if (pThing) //operator bool { //will execute, pThing does not contain nullptr } pThing.release(); //Delete object under management, pThing free again. HOWEVER The point of the story was that smart pointers are only to be used when you need to manage dynamically allocated memory. In your case that doesn't seem to be the case. If your array of Tiles has a size that is known at compile time, just make it a std::array (or your own Array2D that uses std::array under the hood). If the size is unknown at compile time then use std::vector (or your own Vector2D that uses std::vector under the hood). A C style array decays to a pointer: void UseIntArray(int* array, int size) { for (int i = 0; i < size; ++i) { array[i] = //do something... } } int main() { int array[10]; UseIntArray(array, 10); return 0; } C++ actually supports references as a separate concept and those are preferred: void UseIntVector(std::vector<int>& vec) { for (auto& elem : vec) { //Use elem } } int main() { std::vector<int> vec; //populate vector... UseIntVector(vec); return 0; } But you could use pointers if you wanted to: void UseIntVector(std::vector<int>* pVec) { for (auto& elem : *pVec) { //Use elem } } int main() { std::vector<int> vec; //populate vector... UseIntVector(&vec); return 0; } A std::array is somewhat trickier as it's size is part of the template. So you'd either have to write your function to only accept std::arrays of fixed size or the function itself has to be a template: template <std::size_t size> void UseIntArray(std::array<int, size>& array) { for (auto& elem : array) { //Use elem... } } int main() { std::array<int, 100> array; UseIntArray(array); //Template deduction passes along size automatically. return 0; } It's not recommended as it still is not guaranteed to be a continuous block of memory. Many of your technical problems are artificial, stemming from poor overall design, so I'd focus my efforts there. For example, your "map" should probably be a class in it's own right, not a plain 2D array of Tiles. Then the "map" object manages it's implementation details internally and other objects ask the map to perform tasks. This keeps "map"'s implementation details restricted to the "map" class and stops it from being scattered all around the code as it is now. In simple terms, The "Human" object asks a "Dog" object to walk. The "Human" object doesn't go and manipulate the dog's legs directly.
  9. You seem to be compiling with no optimizations ? Compiling without optimizations in gcc: g++ -std=c++1z ltt_test.cpp Indeed, vector is slower. Which is expected for un-optimized code: Vector: 64576298 Array: 46003023 However, once you enable optimizations: g++ -std=c++1z -O2 ltt_test.cpp We get: Vector: 49 Array: 340 Godbolt's compiler explorer shows that the compiler simply throws out the loops completely. Either because it is smart enough to pre-compute the result or because your code invokes undefined behavior (signed int overflow is undefined), so it's a broken program anyway and the compiler can have it's way with it.
  10. std::vector and std::array are just as fast as plain C style arrays. Why would they be any slower? All the abstractions get removed by the compiler until what is left is the plain memory, just like a C style array. If not, you're doing something wrong. Play around with Godbolt's compiler explorer to convince yourself. Unless you ask std::vector or std::array for extra functionality, such as range checking or dynamically allocating memory. Things a plain C array can't even perform, then yes - you pay for that extra functionality.
  11. @fpo Back behind a real keyboard, here goes... 1: Forward declaring First, understand the problem. When your project is compiled, only the cpp files are entry points for the compiler. Each cpp file is compiled separately, without knowledge of the other cpp files. The contents of included files are copied verbatim into the position of the include statement. Thus, if we have some files: Thing.h #ifndef __THING_H__ //These 2 lines are called... #define __THING_H__ //...include guard #include "Tile.h" class Thing { Tile mTile; //Thing contains a Tile instance. }; #endif //__THING_H__ Tile.h #ifndef __TILE_H__ #define __TILE_H__ //Include guard... #include "Thing.h" class Tile { Thing mThing; //Tile contains a Thing instance... }; #endif //__TILE_H__ Main.cpp #include "Thing.h" int main() { Thing aThing; //Create Thing instance... return 0; } The entry point for compilation is Main.cpp, and the preprocessor will expand this into: class Tile { Thing mThing; //Tile contains a Thing instance... }; class Thing { Tile mTile; //Thing contains a Tile instance. }; int main() { Thing aThing; //Create Thing instance... return 0; } This is literally what the compiler will see after the preprocessor is done. Note that: If there were no include guards, this would've led to endless recursion. Thing.h will include Tile.h which includes Thing.h which includes Tile.h and so on forever. class Thing can see the definition of class Tile but class Tile can't see the definition of class Thing. Thus this will fail to compile as it's impossible for Tile to have a instance of unknown class Thing. Forward declaring can help solve this problem because it allows introducing a new name for a class to be defined later. However this means there are limitations to what you can do with a forward declared class: class Thing; //Forward declaration... class Tile { Thing mThing; //Illegal, you can't have a instance of a forward declared class, compiler can't possibly know storage requirements. Thing* mpThing; //Ok, pointers to forward declared classes are fine. Pointers have a fixed size so compiler knows storage requirements. Thing& mrThing; //Ok, references are pointers under the hood. }; Thing SomeFunction(Thing aThing); //Ok, function declaration which uses forward declared type as function parameter and/or return type. Thing AnotherFunction(Thing aThing) {} //Illegal, function definition with forward declared types as parameter and/or return type not allowed. Thing& YetAnotherFunction(Thing* apThing) {} //Ok, function definition with pointers or references to forward declared type as parameter and/or return type allowed, but without //using it's members. Typically, one would forward declare a required class in the header and include the full definition in the code file, where there are no conflicts: Thing.h #ifndef __THING_H__ #define __THING_H__ class Tile; //Forward declare Tile... class Thing { public: Thing(Tile* aTile); private: Tile* mpTile; //Hold a pointer, which is allowed... }; #endif //__THING_H__ Thing.cpp #include "Thing.h" #include "Tile.h" //You can include the full definition of Tile here without problem... Thing::Thing(Tile* aTile) : mpTile(aTile) {} 2: Smart Pointers The purpose of smart pointers is to act as a resource handle for memory. Whenever memory is allocated dynamically it has to be freed at some point. By tying the allocation and freeing of memory to a automatic object on the stack this is handled automatically. This is a implementation of RAII - Resource acquisition is initialization. //Dumb pointer - bad code, don't do this! void DoSomething(Thing* apThing) { if (/*something*/) { throw SomeException; } //use apThing... } void DoSomethingElse(Thing* apThing) { //use apThing... delete apThing; //Take ownership and delete the memory... } void SomeFunction() { Thing* pThing = new Thing; //Manual "old style" allocation. DoSomething(pThing); //If function DoSomething throws an exception, the delete pThing line will never be reached and memory is leaked. DoSomethingElse(pThing); //Code poorly documents itself. Function DoSomethingElse actually takes over ownership of pointer and deletes it. pThing->SomeMember(); //Calling member function on deleted Thing :( delete pThing; //Deleting already deleted memory again is undefined behavior. } //Smart pointer void DoSomething(Thing* apThing) { if (/*something*/) { throw SomeException; } //use apThing... } void DoSomethingElse(std::unique_ptr<Thing> apThing) { //use apThing... //unique_ptr deletes managed object automatically when it goes out of scope. } void SomeFunction() { auto pThing = std::make_unique<Thing>(); DoSomething(pThing.get()); //Function DoSomething takes a plain dumb pointer to a Thing. This makes it clear it only wants to use //the pointer and does not take ownership. If the function throws an exception pThing will automatically be //deleted when it goes out of scope. DoSomethingElse(pThing); //Compile error. Calling by value would copy pThing, after which there would be 2 owners for the same memory. //std::unique_ptr is truly unique and cannot be copied, which is exactly what you want here. DoSomethingElse(std::move(pThing)); //Ok, we move pThing, thereby explicitly handing over ownership to function DoSomethingElse. //No need to manually delete. } Note that plain dumb pointers are still fine in order to refer to some object but without taking responsibility/ownership. Stl containers like std::vector are another example of a resource handle. 3: (2D) arrays One of the reasons we don't really like C style arrays in C++ is because they carry too little information by themselves to be really useful and if one has to manually supply that information you open up the way for errors. For example: void PopulateArray(int* array, int size) { //... } void PrintArray(int* array, int size) { //... } int main() { auto constexpr size = 100; int myArray[size]; PopulateArray(myArray, size); //Manually passing along size each time is cumbersome... PrintArray(myArray, 101); //Uh oh! return 0; } An array name decays into a pointer that, after being passed to a function, has no information about the size of the array. We'd have to pass that information along manually, which is cumbersome and error prone. If we'd have used a std::vector instead, for example, all such problems go away, because std::vector knows it's own size. 2D arrays are even more problematic. Tile** does not mean "Pointer to 2D array of Tile" like most beginners seem to think. It actually means "Array of pointers, each pointing to an array of pointers to Tile", and no-one is guaranteeing it's even square, or that it's even a continuous block of memory for that matter. Worse still, if you ever need to pass such a thing to a function that needs to modify the pointer itself you become a 3-star programmer (Tile***). Any serious programmer will simply stop looking at your code at that point. One of the powers of C++ is that it allows building your own abstractions to hide the complexity. A 2D array can be stored in a plain 1D array of rows * columns size. Write yourself a 2D array class that handles all this complexity for you: template <class T, int rows, int cols> class Array2D { public: const T& Get(int row, int col) const { return mArray[row * cols + col]; } T& Get(int row, int col) { return mArray[row * cols + col]; } private: std::array<T, rows * cols> mArray; }; Which could be used as such: Array2D<int, 5, 10> array; //2D int array 5 rows, 10 columns array.Get(1, 6) = 101; //Assign 101 to item at row 1, column 6 std::cout << array.Get(1, 6); //Print item at row 1, column 6
  12. @fpo Way too much to answer from my phone but 2 red flags that stick out immediately: Handing out pointers to things stored in collections like a std::vector and storing them is a recepy for disaster. std::vector will eventually relocate and invalidate all those pointers. things[0].RequestMove((Tile**)&map[0][0], 10, 10); is plain wrong. &map[0][0] is a *Tile, not a **Tile. Casting it to make the compiler shut up doesnt change that. This is undefined behavior.
  13. In simple terms, mAh is the amount of current, in milliamps, the battery can supply for an hour. So a 1500mAh battery would be able to supply 1.5A for an hour, or 3A for half an hour, etc... (In reality it's not as linear as that, but let's keep it simple). It's an absolute number for a certain battery, regardless of battery voltage. Wh is the amount of energy the battery can supply for an hour. The battery voltage affects this. A 5V, 1500mAh battery would equate to 7500mWh or 7.5Wh. A 10V, 1500mAh battery would be 15000mWh or 15Wh, etc... Phone batteries all tend to be single-cell li-ion these days, which are all 3.7V. Given that the voltage is always the same, I guess they just chose to go with the mAh rating. However, laptop batteries are available in different voltages. The voltage can be whatever the manufacturer chose to go with for that particular laptop. Some are 11.1V, others are 14.8V and so on. In this case it makes more sense to go with the Wh rating.
  14. What is supposed to be constexpr about this? It's just a plain non-const int called g_lerp_enabled.
  15. We already have devices which would be much more efficient at generating electricity in space: solar panels. With earth's atmosphere out of the way, solar panels in space would be much more efficient then down here on earth. The real problems are getting massive amounts of equipment up there, maintenance, and getting the generated power back down here again - all at a cost that can compete with the current systems. (Imho, currently the only viable environmentally neutral way to produce massive amounts of energy is nuclear power. Modern technology is much safer then the old reactors still in use, and can even re-burn the waste of previous generations of reactors to reduce said waste even further. We should stop the current trend of ignoring nuclear power while constantly pushing the retirement date for old obsolete reactors forward because when push comes to shove the renewables are not yet ready to take over - until some accident happens with one of those old reactors that should've already been replaced with a modern one.)
  16. It simply does not provide enough information on the sticker to tell. Power equals voltage times current, thus it says: 79.2W on the 3.3V 110W on the 5V 192W on 12V I/O 216W on 12V CPU However, before you go off thinking this is a 600W PSU, most power supplies don't support maximum loading on all lines at the same time. There's usually some "combined maximum". The sticker does not say anything so it's anyone's guess.
  17. One possibility: #include <iostream> #include <string> int main() { const std::string name = "name=test"; //check if "name=" is present in string and if it starts at position 0. const auto startIndex = name.find("name="); if (startIndex == std::string::npos || startIndex) { std::cout << "Did not find \"name=\" or it was not located at beginning of string.\n"; return 1; } //split string. const auto subName = name.substr(std::string("name=").length()); //optionally test result length. Will be empty if there's nothing after "name=" if (!subName.length()) { std::cout << "Result empty."; return 2; } std::cout << subName << '\n'; return 0; }
  18. You might want to add error handling tough, a quick glimpse at the other code shows it does not check if opening files succeeded before reading/writing , etc...
  19. Something like this ? (enable C++14) #include <iostream> #include <fstream> #include <vector> #include <string> #include <iterator> #include <algorithm> #include <optional> bool SaveStringVectorToFile(const std::vector<std::string>& v, const std::string& fileName) { auto oFile = std::ofstream(fileName); if (!oFile.is_open()) { return false; } std::copy(v.begin(), v.end(), std::ostream_iterator<std::string>(oFile, "\n")); return oFile ? true : false; } std::optional<std::vector<std::string>> LoadStringVectorFromFile(const std::string& fileName) { auto iFile = std::ifstream(fileName); if (!iFile.is_open()) { return std::nullopt; } const auto v = std::vector<std::string>(std::istream_iterator<std::string>(iFile), std::istream_iterator<std::string>()); return iFile.bad() ? std::nullopt : std::make_optional(v); } int main() { //Save existing vector to file... const auto learned_ = std::vector<std::string>{"Hello", "This", "Is", "A", "List"}; if (!SaveStringVectorToFile(learned_, "Test.txt")) { std::cout << "Failed to save to file!\n"; return 1; } //Load new vector from file... const auto fromFile = LoadStringVectorFromFile("Test.txt"); if (!fromFile) { std::cout << "Failed to load from file!\n"; return 2; } //Print loaded contents... std::cout << "Loaded from file: \n"; for (const auto& str : *fromFile) { std::cout << str << '\n'; } return 0; }
  20. Possible implementation: #include <iostream> #include <string> #include <sstream> #include <vector> #include <iterator> #include <algorithm> int main() { const auto testString = std::string("Lorem ipsum dolor sit amet ..."); auto testStringStream = std::stringstream(testString); auto words = std::vector<std::string>( std::istream_iterator<std::string>(testStringStream), std::istream_iterator<std::string>() ); words.erase( std::remove_if(words.begin(), words.end(), [](const std::string& str) { return str.size() <= 3; }), words.end() ); std::cout << "String contains " << words.size() << " words with more then 3 characters.\n"; return 0; } Constructs a stringstream with the given input string, then constructs a vector of strings where each element will be a word from the input string. Then erases all words with less then 4 characters.
  21. It's a hard crash and windows auto-rebooting. The screeching is just the last few milliseconds of audio endlessly repeating as the audio buffers are no longer being refreshed, it's not indicative of anything.
  22. Is the port physically broken or just not working ? In the latter case I'd look into the SOT23-6 device that appears to be sheared off above the port.
  23. Have you tried running the card yet? Looks like it might just be a memory VCC decoupling cap. Should be able to do fine with one less. If you're going to fix it, do not re-use the broken capacitor. MLCC devices are very sensitive to mechanical stress and could easily short.
×