• Advertisement

Again Inclusion question

Recommended Posts

Hi guys, I would like to have the header below:

Paths.h

#pragma once
#include <string>

namespace Paths {
	
	namespace Images {
		static std::string CHECKER = "Graphics/Checker.png";
	}

	namespace Fonts {
		static std::string BUBBLEBATH = "Fonts/BUBBLEBATH.ttf";
	}

}

Included all around the place so that I can do stuff like Paths::Images::CHECKER to easily load the proper file or to request it again by passing that to a map as a key.

Though this I believe create a copy of the static string for every compilation unit, am I correct? Meaning I include it in 10 cpp and get 10 copies of each string.  How would I do this the proper way?

Share this post


Link to post
Share on other sites
Advertisement

Change the "static" with "const"? Or don't use static and const if you still want to be able to change these strings.

If static is used outside of a function or class, it ensures the variable/function can only be used by code in that specific file (translation unit to be precise), and nowhere else. const variables are prevented from changing.

Furthermore, use lower case characters for globals. Most programmers use full upper case for macros. You coud use something like g_checker and g_bubblebath to explicitly indicate these variables are global (though scoped to some namespace). Alternatively, you can consider to use macros for these file names if you intend to add hundreds of them to your code. This will reduce your memory usage.

Edited by matt77hias

Share this post


Link to post
Share on other sites

The exact answer depends on compiler and options you use. Duplicates can be eliminated by compiler/linker, especially if LTO (Link Time Optimization) can be used.

See: "Overhead of static constants when included into many translation units?"

Apparently C++17 introduces inline variables.

inline const std::string my_string = "MyString";

 

Edited by matt77hias

Share this post


Link to post
Share on other sites

Ok I think I've figured it out myself, extern to the rescue :D

Paths.h

#pragma once
#include <string>

namespace Paths {
	
	namespace Images {
		extern std::string CHECKER;
	}

	namespace Fonts {
		extern std::string BUBBLEBATH;
	}

}

Paths.cpp

#include "Paths.h"

std::string Paths::Images::CHECKER = "Graphics/Checker.png";
std::string Paths::Fonts::BUBBLEBATH = "Fonts/BUBBLEBATH.ttf";

 

Share this post


Link to post
Share on other sites
header file

namespace Paths {
	
	namespace Images {
		extern std::string CHECKER;
	}

	namespace Fonts {
		extern std::string BUBBLEBATH;
	}

}
source file 
#include <Paths.h>

namespace Paths {
	
	namespace Images {
		std::string CHECKER = "Graphics/Checker.png";
	}

	namespace Fonts {
		std::string BUBBLEBATH = "Fonts/BUBBLEBATH.ttf";
	}

}

 

Share this post


Link to post
Share on other sites
1 minute ago, MarcusAseth said:

@AxeGuywithanAxe I've beaten you by few seconds  But thanks

Also, any major differences in the way I did it above and in the way you did it? Or is a style choice? 

Yeah, i saw that.. haha... not a difference, just style choice and laziness (I just copied and pasted your original comment and added extern and removed static , etc)

Share this post


Link to post
Share on other sites
7 minutes ago, jpetrie said:

Generally I create free functions to provide well-known constant values that cannot just be constexprs.

@jpetrie can you show me an example of one of this free function?

I would like to understand where my global variables should end up living, at which point during executing are they initialized, how this free functions get to them and how all the other files get to this free functions ^_^'

 

Edited by MarcusAseth

Share this post


Link to post
Share on other sites

Simplest form is to create them inside main(), and pass them around as needed. You could make a class for them, so related variables are close together.

It's not a real global, but you have much more control over them, and you have to make explicit that some code relies on them, which will one day help in refactoring code (ie avoid the case that you got all parameters, and then find out code also has a huge number of dependencies on globals you completely forgot about.)

 

Share this post


Link to post
Share on other sites
32 minutes ago, Alberth said:

Simplest form is to create them inside main(), and pass them around as needed.

Not a big fan of that, some object are just too widely used like App for requesting textures to it and Game to allow entity to interact with other entities in the game or the global paths to be used for loading and requesting textures to App (in the code above), I wouldn't want to pass this stuff down constructors and inheritance chains just to end up storing a pointer to each one of them inside of every object or something. And this in case I have understood what you are saying correctly, for me is always hard without a concrete code example to backup an explanation. :\

Edited by MarcusAseth

Share this post


Link to post
Share on other sites
2 hours ago, MarcusAseth said:

Not a big fan of that, some object are just too widely used

That fact, that they are "widely used," is indicative of a design failure. In a non-trivially-sized project, there are very few things that actually "need" to be accessed from anywhere.

Passing dependencies around like Alberth is suggesting is the better way that making a ton of secret globals. It makes dependencies clear, allows you to enforce contracts in your interfaces, and so on. Globals are impossible to control effectively and impossible to reason about.

 

3 hours ago, MarcusAseth said:

can you show me an example of one of this free function?

 

"SomeThing getSomeThing() { return SomeThing(value, of, some, thing); }" if SomeThing has value semantics or "SomeThing& getSomeThing() { static SomeThing result(value, of, some, thing); return result; }" if it's more complex, et cetera.

 

Share this post


Link to post
Share on other sites

@jpetrie sorry, still not clear ^_^' I guess I am missing the bigger structure here, where everything is placed in comparison to everything else, like in which header is this function compared to who calls it, where are the things result is builted with taken from and how you reach them and so on...

SomeThing& getSomeThing()
{ 
	static SomeThing result(value, of, some, thing);
	return result; 
}

so this above is the function.

So from what I am understanding, in practice would look something like this, and it will live in the Global.h:

Global.h

#include "App.h"
#include "SDL2\SDL.h" 

struct AppInfo {
		App* App;
		GameMode* Game;
		SDL_Window* Window;
		SDL_Renderer* Renderer;
		Uint32 Width, Height;
	};

AppInfo& GetAppInfo()
{ 
	static AppInfo result{MyApp.GetApp(), MyApp.GetGame(), MyApp.GetWindow(), MyApp.GetRenderer(), MyApp.GetWinWidth(), MyApp.GetWinHeight()};
	return result; 
}

But the problem is, when is that static initialized?! If it is when I run the program, then App wouldn't be initialized yet... is that the case? If so, then I still don't get it how to do it...

Furthermore, where is this "App MyApp"  global object created and initialized, I mean where is living? :\

The example is still too abstract for my brain, the problem is I can't see the entire structure pattern here... :\

Edited by MarcusAseth

Share this post


Link to post
Share on other sites

Why not cut using "variables" completly for string constants and use some inline accessor function to grab it?

namespace MyGame
{
   namespace Constants
   {
      inline const char* Files() { return "./Assets"; }
      inline const char* OpenGl() { return "./Lib/OpenGl.dll"; }
   }
}

...
  
void* gl_lib = (void*)LoadLibraryA(MyGame::Constants::OpenGl());

Any clever compiler (and I think even MSVC should solve this) optimizes the inline call away for a pure string constant access without any considerable overhead

Share this post


Link to post
Share on other sites

@Shaarigan if I understand correctly what you're saying, that's seems messy to me because then I have to compose in place the name of the thing I want to load like string(MyGame::Constants::Files()) + "Graphics/Checker.png" and I have to remember the name of stuff all over the place, much easier to just call Paths::Images::CHECKER and let it do the right thing.

Edited by MarcusAseth

Share this post


Link to post
Share on other sites
1 hour ago, MarcusAseth said:

string(MyGame::Constants::Files()) + "Graphics/Checker.png"

Let it return a const std::string instead of a const char *. Then you can already slightly reduce it to

MyGame::Constants::Files() + "Graphics/Checker.png", which looks pretty reasonable (assuming you do not perform this a million times per second).

Share this post


Link to post
Share on other sites
Just now, matt77hias said:

Let it return a const std::string instead of a const char *. Then you can already slightly reduce it to

MyGame::Constants::Files() + "Graphics/Checker.png", which looks pretty reasonable (assuming you do not perform this a million times per second).

Looks still pretty bad to me...All I am doing right now to load an image is this:


//Load Textures
LoadTexture(Paths::Images::CHECKER);

and to get it from another file,

Checker::Checker(float x, float y):
	Tile(x,y,Paths::Images::CHECKER)
{
}

The fact I write it in 2 different places increase chances for mistakes if I where to manually write it.

This way, I simply cannot make a mistake, and if I mispell it inside the Paths.h, then that error get caught by the LoadTexture.

Share this post


Link to post
Share on other sites
4 minutes ago, MarcusAseth said:

The fact I write it in 2 different places increase chances for mistakes if I where to manually write it.

Fair point, but what if you want to change the directory of your assets? How many changes does that inccur at the moment?

You could also encapsulate your resources in some object which provides a method for retrieving the filename, file name and file path. And then you can pass a reference to the resource object around. This avoids the need for duplicating the concatenation everywhere.

Edited by matt77hias

Share this post


Link to post
Share on other sites
2 minutes ago, matt77hias said:

Fair point, but what if you want to change the directory of your assets? How many changes does that inccur at the moment?

Exactly 1.  Hold ALT pressed inside of visual studio, do a rectangle box selection, write the new directory name. And this is why I have them all lined up inside a single file. The way you guy suggest, request go inside of each and every file 1 by 1 and fix it.

Share this post


Link to post
Share on other sites
8 minutes ago, MarcusAseth said:

Exactly 1.  Hold ALT pressed inside of visual studio, do a rectangle box selection, write the new directory name. And this is why I have them all lined up inside a single file. The way you guy suggest, request go inside of each and every file 1 by 1 and fix it.

Ok but in Software Engineering a change is a change independent of some solution (.sln) wide global search-and-replace (which does multiple changes for you). The latter is some handy utility to help you do it efficiently, but it is no remedy for the disease (referring to implicit dependencies).

Edited by matt77hias

Share this post


Link to post
Share on other sites

Why are you loading textures manually in one place, but also passing the path in a different (seemingly unrelated) place? Your original post says it's to be able to request the texture again, but why would you ever want that?

I would have expected the tile to only hold a pointer to its texture. Upon creation, load the texture (if need be, have some caching system or similar to prevent multiple loads of the same texture) and store a pointer to the texture in each tile/object.

Share this post


Link to post
Share on other sites

I wouldnt trust C++11/14/17 support anyways but thats another topic.

To quote Hodgman, game IO should be a lot more limited than generic IO and so your game should anyways NOT use full paths to read Assets from disk. What I do in my framework is to set a fixed working directory and address my assets relative to that using "./" if necessary  (to address packages)

I do not fully understand objections because however you change paths/files, you do it in one single file used in many other files anyways so changing a string constant or function return should be regardless. However, global string constants have disadvantages as mentioned in any above post due to initialization order and local unit copies where inline header functions are optimized away mostly and directly accessed without code impact. Another option is for sure using macro constants so you wont have any access overhead.

It is ok for small projects and debugging code to go for direct file access but in medium or larger ones there should at least be some kind of package for disk aligning and IO performance reasons

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now


  • Advertisement
  • Advertisement
  • Popular Tags

  • Advertisement
  • Popular Now

  • Similar Content

    • By mister345
      Hi guys, so I have about 200 files isolated in their own folder [physics code] in my Visual Studio project that I never touch. They might as well be a separate library, I just keep em as source files in case I need to look at them or step through them, but I will never actually edit them, so there's no need to ever build them.
      However, when I need to rebuild the entire solution because I changed the other files, all of these 200 files get rebuilt too and it takes a really long time.
      If I click on their properties -> exclude from build, then rebuild, it's no good because then all the previous built objects get deleted automatically, so the build will fail.
      So how do I make the built versions of the 200+ files in the physics directory stay where they are so I never have to rebuild them, but
      do a normal rebuild for everything else? Any easy answers to this? The simpler the better, as I am a noob at Visual Studio settings. Thanks.
    • By Snaked
      Im working in this project for 1 year .... mostly i develop a tool and databases for make the different maps and now i'm doing the client for play the game
      Tell me if you like it......
      this is a capture of how is viewing atm

       
       
      https://youtu.be/9251v4wDTQ0
    • By reenigne
      For those that don't know me. I am the individual who's two videos are listed here under setup for https://wiki.libsdl.org/Tutorials
      I also run grhmedia.com where I host the projects and code for the tutorials I have online.
      Recently, I received a notice from youtube they will be implementing their new policy in protecting video content as of which I won't be monetized till I meat there required number of viewers and views each month.

      Frankly, I'm pretty sick of youtube. I put up a video and someone else learns from it and puts up another video and because of the way youtube does their placement they end up with more views.
      Even guys that clearly post false information such as one individual who said GLEW 2.0 was broken because he didn't know how to compile it. He in short didn't know how to modify the script he used because he didn't understand make files and how the requirements of the compiler and library changes needed some different flags.

      At the end of the month when they implement this I will take down the content and host on my own server purely and it will be a paid system and or patreon. 

      I get my videos may be a bit dry, I generally figure people are there to learn how to do something and I rather not waste their time. 
      I used to also help people for free even those coming from the other videos. That won't be the case any more. I used to just take anyone emails and work with them my email is posted on the site.

      I don't expect to get the required number of subscribers in that time or increased views. Even if I did well it wouldn't take care of each reoccurring month.
      I figure this is simpler and I don't plan on putting some sort of exorbitant fee for a monthly subscription or the like.
      I was thinking on the lines of a few dollars 1,2, and 3 and the larger subscription gets you assistance with the content in the tutorials if needed that month.
      Maybe another fee if it is related but not directly in the content. 
      The fees would serve to cut down on the number of people who ask for help and maybe encourage some of the people to actually pay attention to what is said rather than do their own thing. That actually turns out to be 90% of the issues. I spent 6 hours helping one individual last week I must have asked him 20 times did you do exactly like I said in the video even pointed directly to the section. When he finally sent me a copy of the what he entered I knew then and there he had not. I circled it and I pointed out that wasn't what I said to do in the video. I didn't tell him what was wrong and how I knew that way he would go back and actually follow what it said to do. He then reported it worked. Yea, no kidding following directions works. But hey isn't alone and well its part of the learning process.

      So the point of this isn't to be a gripe session. I'm just looking for a bit of feed back. Do you think the fees are unreasonable?
      Should I keep the youtube channel and do just the fees with patreon or do you think locking the content to my site and require a subscription is an idea.

      I'm just looking at the fact it is unrealistic to think youtube/google will actually get stuff right or that youtube viewers will actually bother to start looking for more accurate videos. 
    • By mister345
      Hi, can someone please explain why this is giving an assertion EyePosition!=0 exception?
       
      _lightBufferVS->viewMatrix = DirectX::XMMatrixLookAtLH(XMLoadFloat3(&_lightBufferVS->position), XMLoadFloat3(&_lookAt), XMLoadFloat3(&up));
      It looks like DirectX doesnt want the 2nd parameter to be a zero vector in the assertion, but I passed in a zero vector with this exact same code in another program and it ran just fine. (Here is the version of the code that worked - note XMLoadFloat3(&m_lookAt) parameter value is (0,0,0) at runtime - I debugged it - but it throws no exceptions.
          m_viewMatrix = DirectX::XMMatrixLookAtLH(XMLoadFloat3(&m_position), XMLoadFloat3(&m_lookAt), XMLoadFloat3(&up)); Here is the repo for the broken code (See LightClass) https://github.com/mister51213/DirectX11Engine/blob/master/DirectX11Engine/LightClass.cpp
      and here is the repo with the alternative version of the code that is working with a value of (0,0,0) for the second parameter.
      https://github.com/mister51213/DX11Port_SoftShadows/blob/master/Engine/lightclass.cpp
    • By Rannion
      Hi, I am sending data to peers and those data need to be retreived from a scenegraph with a mutex to lock the data.
      The process of gathering the data is taking a bit less than a ms. I'm starting the thread every time I want to gather the data. If I'm running at 60 fps, I'm starting the thread 60 times per second so is that a performance or design problem?
      Would it be much better to have the thread always running and some kind of mechanism to ask him to perform the task whenever it's needed, so around 60 or 120fps?
      Also, does starting a thread creates some memory alloc/dealloc and then produce on the long run some kind of fragmentation?
      Thank you all.
  • Advertisement