Jump to content

  • Log In with Google      Sign In   
  • Create Account

Servant of the Lord

Member Since 24 Sep 2005
Offline Last Active Yesterday, 07:02 PM

#5300461 sort list

Posted by Servant of the Lord on 12 July 2016 - 07:18 PM

I'm glad you got it working; congrats!  :)

 

You're using C++, so you should use:

const int MAX_TEXTURES = 50;

You shouldn't use #define, unless you need a #define, which is very rare. Or rather, should be rare if you are coding properly.

 

Alot of old books and tutorials suggest #define MAX_TEXTURES 50, but that's considered bad practice now.

 

(this is programming for ya: Everytime you post something, someone will tell you it's outdated and show you a better way. When starting out, this can feel frustrating ("I'm always doing everything wrong!"), but once you get used to it, it's pretty cool, because you are always learning new things, and (almost always) the new things people show you actually are better!  ^_^)




#5300186 sort list

Posted by Servant of the Lord on 11 July 2016 - 09:01 AM

I use std::vectors and sort them with std::sort regularly, sorting them by all kinds of criteria. It works fine.

 

Part of the problem here, is programming is built up layer by layer from simple concepts to form more complex ideas. You've skipped learning the simple concepts that the complex ideas are built from, which makes the complex ideas seem confusing and difficult.  :(

 

The idea of separation between iterators (std::begin()/std::end()), containers (std::vector/std::list), and algorithms (std::sort()) is important.

The idea of passing code as parameters (via functions/functors/lambdas) is incredibly important, and this is what my lambda in my post was, and your bool compare (const Ausfaelle &ausfaelle1, const Ausfaelle &ausfaelle2) function was.

 

The design of (this part of) the libraries isn't stupid, it's just you stepped some of the learning steps. Multiplication is extra difficult if you don't know addition, because multiplication is built from addition.

 

Re-read my post, and I'll give you two tips:

 

1) Never call std::sort() while looping over the elements that you are sorting. std::sort already includes the loop for you inside of it.

Don't do this:

for(every element in myContainer)
{
     std::sort(...myContainer...); //BAD!
}

Do this: 

std::sort(...myContainer...); //GOOD!

(this is psuedocode, not real code)

 

2) Your 'color' is just an integer (1, 2, 3, 4, etc...). You don't need to do anything special to sort multiple integers. Your compare() function, while only taking two parameters, is used by std::sort() to sort the entire list, and to sort any number you give it.

 

 

If you still need help, post the chunk of code (and the ten lines of code before and after it) where you are trying to sort your list.




#5299789 sort list

Posted by Servant of the Lord on 08 July 2016 - 10:31 AM

First, you probably should be using std::vector, unless you have a reason why you need to use std::list.

Use 'auto' instead of 'std::list<Ausfaelle>::iterator', it'll make your code easier to read.

Also use 'range-for' for() loops if you want to iterate over every element in a container (I'll so an example of this below).

 

Run this code to understand how two nested for() loops work. Currently you are doing each image 10 times.

 

I'm not 100% sure what you are going for, but I think this is closer to what you are wanting:

int x = 0; //Initialize 'x'.

//This is a range-for(). It loops over every element of 'Ausfalllinie',
//and makes 'ausfaelle' be a reference to each element.
for(const Ausfaelle &ausfaelle : Ausfalllinie) 
{
    if(ausfaelle.drValue >= 0.2f)
    {
        //...
    }
    
    //Increment 'x', so each 'x' is larger than than the previous loop/iteration's 'x'.
    ++x; //Same as x = (x + 1);
}

As for sorting your container, if you use a std::vector, it should look like this:

std::sort(begin(Ausfalllinie), end(Ausfalllinie),
[](const Ausfaelle &ausfaelle1, const Ausfaelle &ausfaelle2)
{
    return (ausfaelle1.color < ausfaelle2.color); //Return true if 'ausfaelle1' should be sorted before 'ausfaelle2'.
});

If you use a std::list, it should look like this:

Ausfalllinie.sort([](const Ausfaelle &ausfaelle1, const Ausfaelle &ausfaelle2)
{
    return (ausfaelle1.color < ausfaelle2.color); //Return true if 'ausfaelle1' should be sorted before 'ausfaelle2'.
});



#5299651 sort list

Posted by Servant of the Lord on 07 July 2016 - 11:50 AM

No, std::sort requires random access iterators. std::list only provides bidirectional iterators, which means you can't use them with std::sort. Use std::list::sort instead.

 

*facepalm* - Mea culpa.

I almost always use std::vector - I forgot about the iterator requirements.




#5299633 sort list

Posted by Servant of the Lord on 07 July 2016 - 11:06 AM

std::sort() does this for you. You can pass it a custom sort function.

 

The syntax is:

 std::sort(begin(myList), end(myList), YourSortFunction);

Using a lambda it can look like this:

struct MyStruct
{
   float value;
};

std::list<MyStruct> myList;

std::sort(begin(myList), end(myList),
[](const MyStruct &structA, const MyStruct &structB)
{
    return (structA.value < structB.value); //Replace with your sort criteria.
});

Note: Instead of using container.begin() and container.end(), the newest guidelines say to use std::begin(container) and std::end(container). It's more flexible, and works with things like normal arrays, and so on.

 

In the majority of situations where you'll be using it, you won't have to type std::, because the compiler can resolve that namespace for you on the begin()/end() functions.




#5299078 Creating Entities from XML in a Data-Oriented ECS

Posted by Servant of the Lord on 04 July 2016 - 09:01 PM

you appear to reach the same conclusion I do, albeit via a differing path; a massive collection of all your systems,

Someone somewhere has to own the systems. Unless you make them inherit from a base class for no reason (which has its own downsides), some class will have a concrete instance of the system. 
 

and a parse that becomes a massive collection of ifs. If there isn't a better way than this, so be it, but I was under the impression there was something more clever to be done. Perhaps I was just overthinking it.

 

There's always a balance between too concrete and too abstract - and that balance might vary depending on the project.

 

You could make your properties all be 100% scriptable. It's not what I would personally code, but whatever floats your ints.

Thief used that method, IIRC (see Doug Church's slides (as archived on Chris Hecker's site)).

 

Personally, I'd hardcode alot of important values, but also enable purely scriptable values (for rapid design), and if certain values get used frequently, I'd make them hardcoded.

 

You can break up your massive if()'s in at least two different ways:

1) You can make each system handle its own property loading by passing the property node to the CreateComponent() function.

or 2) You can design a generic serializable property system for the components, that works even with hardcoded variables.




#5299028 Creating Entities from XML in a Data-Oriented ECS

Posted by Servant of the Lord on 04 July 2016 - 11:41 AM

I'd try it like this: Each system stores its components privately/internally in whatever way that system sees fit. I wouldn't hand out the internal table. Instead, you could query the system for a specific component by ID.
 
Something like this:

class MeowSystem
{
public:
     struct Component
     {
          std::string purringNoise;
     };
 
public:
    //Creates and returns a component for 'entityID'.
    Component &CreateComponent(EntityID entityID);
 
    //If 'entityID' has a Meow component, deletes it. If no such component exists, safely does nothing.
    void DeleteComponent(EntityID entityID);
 
    //If 'entityID' has a Meow component, returns a pointer to it. If no such component exists, returns null.
    Component *GetComponent(EntityID entityID);
    
    //Processes all the components in a data-oriented (i.e. consumption-optimized) way.
    void Purocess(...parameters...);
    
private:
     std::vector<Component> components;
 
     //By decoupling array indices from entity IDs, it gives each individual system greater flexible for optimizations.
     //We optimize for processing the components, rather than accessing the components by ID.
     std::unordered_map<EntityID, ArrayIndex> indexMap;
};
 
typedef MeowSystem::Component MeowComponent;
 
class Systems
{
public:
    MeowSystem meowSystem;
    PhysicsSystem physicsSystem;
    ScriptSystem scriptSystem;
 
    EntityIdPool idPool; //GetNewID(), ReleaseID(id), IsActive(id)
 
public:
    EntityID CreateEntityFromWrongFileFormatForTheJob(std::string xmlFilepath)
    {
        //...blah blah make sure file exists, loads, and parses fine...
        //Actually I wouldn't even pass in a filepath, but that's a different discussion.

        EntityID newID = idPool.GetNewID();
         
        if(file.Has("inheritance"))
        {
            //...create the components that are being inherited, first, and apply the inherited values.
        }

        if(file.Has("meow"))
        {
            MeowComponent &meowComp = meowSystem.CreateComponent(newID); //The file format's 'meow' node could even be passed into 'CreateComponent(id, &node)'.
            meowComp.purringNoise = file.Get("meow.noise").AsString();
        }

        if(file.Has("scripting"))
        {
            ScriptComponent &scriptComp = scriptSystem.CreateComponent(newID);
            scriptComp.scriptName = file.Get("scripting.scriptName").AsString();
            scriptComp.parameters = file.Get("scripting.parameters").AsScriptParameters();
        }

        if(...)
        {
            //...etc...
        }

        return newID;
    }

};

One example of optimization made possible by hiding internal tables

Why I wouldn't pass in a filepath (actually this doesn't really explain the 'why' but it's something I agree with)

 

As for XML, it's not really designed for this. It's intended to be a document markup (like RTF or HTML), even if it's widely popular to twist it for other uses (even by major corporations - Microsoft <_<). It's also not really optimized for human legibility, so it's harder to spot mistakes as there is unnecessary visual clutter (even if you get used to it). But that's a nitpick. The pro is that everyone else overuses XML also, so there's plenty of tools to help you improperly use it :P.




#5298755 Does adding Delegates/Function pointers to an entity break ECS ideology?

Posted by Servant of the Lord on 01 July 2016 - 06:52 PM

If I don't use function pointers what is the alternative? Subclassess of components?

Like the others, I'm not sure what precisely you are talking about.

My guess is, you're saying that you have a bunch of entities that all have SpecialComponent, and that sometimes SpecialComponent needs to do BehaviorA, and other times SpecialComponent needs to do BehaviorB.

If so, one of the key things of some ECS architecture (which vary incredibly widely) is that components don't have logic, they are just data, and the systems handle all the logic.

This means, I'd write the solution like this:

class SpecialComponentSystem
{
private:
   container<SpecialComponent> components;
   
public:
   void ProcessEntities()
   {
       //Process all BehaviorA components...
       for(size_t i = startOfBehaviorAComponents; i < endOfBehaviorAComponents; ++i) //(Actually I'd write the for() statement itself differently too =P)
       {
           components[i].value = (x + q * 3.57f);
           components[i].kittens = (Moar / meow);
       }

       //Process all BehaviorB components...
       for(size_t i = startOfBehaviorBComponents; i < endOfBehaviorBComponents; ++i)
       {
           components[i].value = x;
           components[i].kittens = None;
       }
   }
}

I'd guarantee ahead of time (without having to sort every frame) that all components are grouped in contiguous memory by their behavior (i.e. all BehaviorA's in a row, then all BehaviorB's). This can occur, because each System can store its components in a different order: each System has control (hidden away, unexposed) of how its entities are stored. This means the System can reorder the components differently then the order of EntityIDs.

It can even store them like this:

class SpecialComponentSystem
{
private: //Privacy is important for System-specific memory layout of components.
   std::vector<SpecialComponent> componentsWithBehaviorA;
   std::vector<SpecialComponent> componentsWithBehaviorB;
};

Essentially, you want to have short compact code, without zero branching, and to reduce function call overhead and other operations as much as possible (ofcourse balancing that 'requirement' with all the other requirements of programming, like DRY, single-responsibility, and clean legible ease-to-maintain-and-expand code).
 
Depending on what you are doing: no function calls, zero branching, and so on, isn't always possible.
 
For things like game-related logic, it's understandable that things are going to branch alot more, and probably make calls into scripts or other pluggable behavior. In that case, one solution is just to reconsider how often you need to run that particular logic.
Maybe scripted logic only needs to be run 10 times a second, and maybe pathfinding only needs to be updated once a second.
 
Not every subsystem needs to be called every tick.
 
Sometimes if you twist the way you look at your entities, looking at them as a collection of the same data, you can more easily spot redundant processing.
And ofcourse, don't optimize prematurely, unless you're pretty confident the optimization is actually needed - and even then, profile.  :)




#5298415 Blending order

Posted by Servant of the Lord on 28 June 2016 - 11:21 AM

There are "order independent transparency" methods, but they are more complicated, require more resources, and have corner-cases where they give wrong results.
So, in general, you need to render opaque first.

Okay, so the basic idea is - first render all opaque, then all transparent (no difference in order of transparent) - profit?


Render all opaque (preferably front to back, for speed reasons, but it's not required).
Render all transparent objects (must be back to front, or the visuals will be wrong).


#5298396 PowerUp stuck

Posted by Servant of the Lord on 28 June 2016 - 09:47 AM

 

There's many ways to go about it - so I'm not fixated on maps... but if I may inquire, why don't you like maps?

I think its because its kinda confusing. And I really dont use it much. I am more familiar with list though. And even if i have a chance I opt to list always.

 


Certainly. It's a different kind of tool, but useful to know.

As you know, a list holds 'values' at different 'indexes'.

myList[2] = "blue"; //Assigns the value "blue" to the element located at index '2'.

Maps also hold values, but they use 'keys' instead of indices. This means, we can map any value to any key, and a key can be something other than a number.

myMap["color"] = "blue"; //Assigns the value "blue" to the key "color".

Another thing with maps is that because the map doesn't need the keys to be in linear progression (3, 4, 5, etc...), you can do things like this:

myMap[34261] = "blue"; //Assigns the value "blue" to the key 34261, *without* the map having to be over 34,000 elements long.

It's a very useful tool in many circumstances.

Here's some examples:

textureMap[textureID] = LoadTexture(...); //Maps texture IDs to actual textures.
//Stores templates for types of enemies in a map, using the enemy type name ("goblin") as a key.
enemyTemplateMap["goblin"].LoadFromFile("Enemies/goblin_template.cfg"); 

//Looks up the "goblin" template, and uses that template to create a goblin enemy and map it to an enemy ID.
enemyMap[enemyID] = enemyTemplateMap["goblin"].CreateEnemy(); 

Can you alos elaborate what do you mean by end time and the "amount time left"? quite confuse


There's at least two ways to have a timer do something:

//When activated:
{
    powerUp.timeRemaining = 30 seconds;
    ...make paddle larger...
}

//Every frame:
{
    powerUp.timeRemaining -= deltaTime;
    if(powerUp.timeRemaining <= 0)
    {
       ...Our timer is done, so make the paddle normal size again...
    }
}

The above is useful in some situations, but normally I prefer this second method:

//When activated:
{
    //We store the absolute time the powerup ends, not the relative amount of time remaining...
    powerUp.endTime = (currentTime + 30 seconds);
    ...make paddle larger...
}

//Every frame:
{
    //We don't need to substract anything anymore.
    //powerUp.timeUntilDone -= deltaTime;

    //Instead, we just check if we've passed the end time (i.e. we check if the present time is larger than the ending time).
    if(powerUp.endTime < currentTime)
    {
       ...Our timer is done, so make the paddle normal size again...
    }
}

Note: 'deltaTime' is the amount of time between the previous update and this current update. You calculate it like this:

currentFrameTime = gameTime.ElapsedGameTime.TotalSeconds;
deltaTime = (currentFrameTime - previousFrameTime);
previousFrameTime = currentFrameTime;



#5298312 Is using one the switch statement better then using multiple if statements?

Posted by Servant of the Lord on 27 June 2016 - 06:09 PM

But really that is quite pointless, I think we all can agree that if talking about switch we are talking about switch with "break" labels. Not having break from AFAIK is mostly bugs, or shorthands for reducing duplicate code if blocks are shared

Or to trip up students with Duff's Device.  :lol: 
 

@Servant:

If you are using switch() statements for micro-optimizations, there are other tricks to be aware of also; putting your more-frequently used branches closer to the top of the switch() supposedly helps, though I've never tried it.

 
Does it? I've heard that this can help for regular if/else-chains (on said "dumb" compilers), but from switch all I've heard is that you should put labels in order, like always go 0 on top to 10 on bottom, best case without holes (so 0..10 or 10...0 is optimal, "10, 5, 9, 1" not so much). Reason I've read is that this makes it much easier for calculating where to jump to for the compiler, though I belive any compiler where if/else can be compiled to same assembly as switch should be able to figure out ordering on their own.

I've heard both, but have tested neither.  :) 

Maybe the assumption is that the ones near the top (if the compiler doesn't reorder them) are more likely to have their instructions already loaded into the instruction cache? I have no clue; in either case, I'd rather optimize at the function and system level, and if I have to do micro-optimizations (which I never have needed to do), I'd use explicit compiler intrinisics and play with PGO to see if that provides any tangible benefits.




#5298288 Spreading value x randomly between x amount of elements

Posted by Servant of the Lord on 27 June 2016 - 03:40 PM

Here's my approach. I shuffle the order the elements are visited, to alleviate bias towards the end of the vector.
(The bias is still there for whatever elements I give first shot at the values, but those elements are randomly distributed throughout the vector instead of grouped by order of first-come-first-serve)
 
I also applied a little (optional) fudgery to the maximum amount assigned each element, so the results aren't quite as extreme.

#include <iostream>
#include <vector>
#include <random> //For std::mt19937, std::random_device(), and std::uniform_int_distribution()
#include <algorithm> //For std::shuffle

void FillBuckets(std::vector<int> &buckets, const int amountToDistribute)
{
	std::mt19937 randomGenerator(std::random_device{}());
	
	//We generate some indices to fill the bucket elements in a random order.
	std::vector<size_t> bucketIndices(buckets.size());
	std::iota(begin(bucketIndices), end(bucketIndices), 0);
	std::shuffle(begin(bucketIndices), end(bucketIndices), randomGenerator);
	
	int bucketsRemaining = static_cast<int>(bucketIndices.size());
	int amountRemaining = amountToDistribute;
	
	for(size_t index : bucketIndices)
	{
		int amountToGive = 0;
		
		//If this isn't the last bucket, take a random amount of the remaining value.
		if(bucketsRemaining > 1)
		{
			//Balances out the numbers a bit more, so the first few buckets don't steal everything.
			//This means, if there are two buckets remaining, one of the buckets can take 100%.
			//If there are three buckets remaining, the most each bucket can take is 50%.
			//If there are four buckets remaining, the most each bucket can take is 33%, and so on.
			int maxToGive = (amountRemaining / (bucketsRemaining-1));
			amountToGive = std::uniform_int_distribution<int>(0, maxToGive)(randomGenerator);
		}
		//If this IS the last bucket, just take everything that remains.
		else
		{
			amountToGive = amountRemaining;
		}
		
		buckets[index] = amountToGive;
		amountRemaining -= amountToGive;
		
		bucketsRemaining--;
	}
}

int main()
{
	std::vector<int> buckets(10);
	FillBuckets(buckets, 100);
	
	std::cout << "Result: ";
	for(int amount : buckets)
	{
		std::cout << amount << " ";
	}
	std::cout << std::endl;
	
	return 0;
}



#5298264 Is using one the switch statement better then using multiple if statements?

Posted by Servant of the Lord on 27 June 2016 - 11:56 AM

 

I cannot spot when and how switch can have outperformed proper conditionals in any way on any platform- and by my logic I conclude it cannot.

The reason has already been covered in this very thread: switches are easy to convert to lookup tables in machine code. Conditional statements are less easy. So in a common case you will get better machine code generated for a switch than a if/else ladder. Some compilers are better at this than others.

And the reason why it is easier for compilers to optimize it is because it gives more information for the compiler to work with, so they can detect common patterns of behavior easier that the compiler is familiar with and knows optimization tricks for.

For the same reason, in C++, these two do the same logic:

for(size_t i = 0; i < array.size(); ++i) //Regular 'for' statement
{
   Element &element = array[i];
   Meow(element);
}

for(Element &element : array) //Range-for statement
{
   Meow(element);
}

...but beside them doing the same logic, the second version is easier for the compiler to optimize, because the structure of the code itself communicates information to the compiler. The second version guarantees to the compiler that every element will only be visited once, and that the elements will be traversed in a specific order, and that all the elements are known in advance - the first version guarantees none of those things.

 

In simple cases, the compiler will likely optimize them both the same, but in more complex cases, the compiler may not be able to figure out the optimization of first example, and so may use slightly lesser optimizations instead. This may result in super teeny-tiny gains or losses in performance, which 99.999% of the time don't matter.

 

Basically, the structure of our code becomes contracts to the compiler making guarantees that help the compiler guide the optimizations. Different code structures make different guarantees.

 

else-if() chains make different guarantees than a switch() does, and the extra information switch() communicates can help the compiler more easily recognize certain types of optimizations that else-if() chains might, in some cases, be more harder for the compiler to detect.

 

But if the OP is asking "Which one should I use?", then the usual criteria applies: Use whatever is cleaner/clearer to whoever has to read and expand and maintain the code, and don't optimize prematurely. When it comes time to optimize, profile first to find out what is slow, then focus on the critical parts, and after realizing that architectural macro-optimizations benefit way more than function-level micro-optimizations, and finally-finally-finally, micro-optimize only the areas that require it.

 

In my own code, I tend to find else-if() chains more visually appealing in some cases, and switch()'s cleaner in other cases, and so have plenty of each.

And every one of switch statements used in my code, 100% of the time they were chosen because it makes the code clearer, not for any optimization purposes.

 

If you are using switch() statements for micro-optimizations, there are other tricks to be aware of also; putting your more-frequently used branches closer to the top of the switch() supposedly helps, though I've never tried it. When you get to that level of micro-optimizing though, that's when something like profile-guided-optimizations provide the compiler with even better information about the real-world usage your code would be put through.




#5298158 PowerUp stuck

Posted by Servant of the Lord on 26 June 2016 - 05:06 PM

What about storing the "end time" (which only is changed when you pick up a powerup) instead of storing the "amount of time left" (which has to be updated every frame).

 

Then, store it in a map.

 

Something like this: (psuedocode)

PowerUpType { LargerPaddle, SmallerPaddle, FasterPaddleMovement, SlowerBalls}

Map<PowerUpTime, Time> powerUpActivationMap;

void Game::PickUpPowerUp(PowerUpType type, Time powerUpDuration)
{
    powerUpActivationMap[type] = (currentTime() + powerUpDuration);
}

bool Game::PowerUpIsActive(PowerUpType type)
{
    return (powerUpActivationMap[type] > currentTime());
}

int Game::GetBallSpeed()
{
     if(PowerUpIsActive(SlowerBalls))
          return BallSpeed_Slower;
     
     return BallSpeed_Regular;
}

int Game::GetPaddleSize()
{
     bool largerPaddle = PowerUpIsActive(LargerPaddle);
     bool smallerPaddle = PowerUpIsActive(SmallerPaddle));
     
     if(largerPaddle AND smallerPaddle) return PaddleSize_Regular;
     if(largerPaddle)  return PaddleSize_Larger;
     if(smallerPaddle) return PaddleSize_Smaller;

     return PaddleSize_Regular;
}



#5298023 Including from the Standard C++ Library

Posted by Servant of the Lord on 25 June 2016 - 02:07 PM

When we include libraries from the standard library (like cmath or cstring), what are we actually including?


First, understand that using different brackets in #include "header" and #include <header> only affects the order that the compiler looks through different directories when searching for the files.
Second, understand that you can use different file extensions (in your own headers) and everything will still work fine (as long as the file actually exists with that extension): myHeader, myHeader.h, myHeader.hpp, myHeader.pch, myHeader.meow, whatever - they are just filenames. Occasionally I've even had to #include a .cpp file from one .cpp to another. They're just text files.

Finally, what *actually* happens vary from compiler to compiler, because C++ only enforces behavior not implementation details. Also, the standard library is (mostly) separate from the compiler, even though it is shipped with compilers. For example, Visual Studio and GCC use *different* standard library implementations even though they behave the same and have the same interface. (GCC defaults to using it's own opensource libstdc++ library, Clang defaults to usibng its own opensource libc++ library, and Visual Studio defaults to using a custom-modified version of the third-party proprietary Dinkumware one. There are also other implementations of the standard library available).

The header files actually exist (usually), and (on Windows) they are usually located where you installed your compiler, unless you manually installed the libraries somewhere else.

In my QtCreator install of MinGW, on Win10, I find <cmath> located in:
C:\Qt\Qt5.5.0\Tools\mingw492_32\i686-w64-mingw32\include\c++

At that location, I find a text file named 'cmath' with no extension (I also find 'cstring' and 'iostream' and 'vector' and so on - text files without filename extensions).
It #includes "math.h", undefines some math.h defines (like cos(), tan(), etc...), reimplements them as real functions (instead of macroes), and puts them inside of the std namespace.

 

Depending on the compiler and header, the compiler can also just pretend behind the scenes that the header file really exists, but really do something else instead as long as the behavior is the same. For example, why recompile the standard library headers over and over again? Maybe the compiler just holds them pre-compiled, if it can detect that you aren't doing something that'd require it to recompile the header (like #defining certain macroes).
 

I'm assuming they aren't header files since there is no ".h" extension. Also, how can we just include files and be able to access functions from that library? Are the function definitions inside the included file? I thought it was good practice to separate declaration from definition?

 

First, a huge amount of the standard library classes and functions are templates. Templates in C++ have to be in headers, declaration-and-definition together. It's an unfortunate consequence of how templates work. So for those classes, whether standard library or 3rd party library or your own code, the header is plenty enough, no .cpp needed.

 

Second, for the functions that aren't templated, and have concrete definitions, they basically fall into several categories: If they are inline, or if the compiler wants to inline them for performance reasons, the compiler will generate the code inside your project's .exe - often those kinds of functions are only a few lines of code, so the performance gains outweigh the minor costs. This is common with compiler intrinsic functions.

For other functions, they can be handled just like regular static libraries (compiled into your code, but not inline), or dynamic libraries.

 

If they are linked to as dynamic libraries, then you have to make sure that .DLL actually is available for your project to find, when you run your executable. For Microsoft, the standard library DLLs are just shipped with Windows as part of the OS or as part of the Visual Studio runtimes.

 

For me, using GCC/MinGW, I have to include the standard library DLLs with my .exe. GCC's is named libstdc++-6.dll, which in-turn calls functions from some of Microsoft's libraries, since it has to function on Windows and some code has to run through the OS (like loading files).

 

You can also tell your compiler to statically include the standard library inside your .exe, but that isn't without tradeoffs.






PARTNERS