• Advertisement
Sign in to follow this  

VC++ Shared Project for Engine

This topic is 403 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I am using a visual studio shared project for storing all my engine .h and .cpp files - this was fine at start because i had one executable using this. Now i have 3 executables all referencing the shared project.

 

It seems that when i compile a project, every file from the shared project will be treated as fully included in the project, even though i may just use two or three independent header files.

 

This is increases compile times a lot, but the bad thing is i am required to include two external global variables (Defined in two header files) in every executable  - even when i dont need them or are not a dependency for the header files i am using.

 

Now my question, is there a way to prevent this? I dont want to include the full engine source just for using two header files in a app which uses just a few independent header files... but i dont mind compiling the translation units multiple times - i have a pretty fast dev system.

Edited by Finalspace

Share this post


Link to post
Share on other sites
Advertisement

Btw: I found a workaround:

- I use one win32 project only

- I use one translation unit only

- Everything else is a header file without any .cpp files

- Forget about visibility, make every method static (exluding the inline ones)

- Done

 

Result: Compiles much faster and is much easier to maintain and debug.

 

Downside: Bad when i have multiple executables. In this case i still require a shared project for shared stuff.

Edited by Finalspace

Share this post


Link to post
Share on other sites

i would say instead of a shared project, make it into a library.  would only have to compile it once (unless you modify its code).  and then just link it to the project that needs it

Share this post


Link to post
Share on other sites

There are a lot of weird questions that come out of this.

If there is just a small subset of code that you need to share, that should probably have gone in its own project. But maybe that's not an option if you're trying to just 'dip in' to occasional header files. But if it's only headers you care about, you don't need them to be in a project at all, and if they're already in a project, that project doesn't need to be in the current solution. I don't need to rebuild the C standard library or include a project for it just to #include <stdio.h>, for example.

Even so, if a project is included in the solution, this shouldn't increase compile times. If the relevant cpp files are already built, they won't build again unless you are changing those headers. They won't be linked in either, unless they're being referenced. If you are changing the header, then you probably should be building those files anyway, sooner or later.

As for the globals, the best answer is to not use globals. But you could also have used #ifdef to exclude them.

Share this post


Link to post
Share on other sites

There are a lot of weird questions that come out of this.

If there is just a small subset of code that you need to share, that should probably have gone in its own project. But maybe that's not an option if you're trying to just 'dip in' to occasional header files. But if it's only headers you care about, you don't need them to be in a project at all, and if they're already in a project, that project doesn't need to be in the current solution. I don't need to rebuild the C standard library or include a project for it just to #include <stdio.h>, for example.

Even so, if a project is included in the solution, this shouldn't increase compile times. If the relevant cpp files are already built, they won't build again unless you are changing those headers. They won't be linked in either, unless they're being referenced. If you are changing the header, then you probably should be building those files anyway, sooner or later.

As for the globals, the best answer is to not use globals. But you could also have used #ifdef to exclude them.

 

Indeed i could move some things out into static libraries, for example the physics system is a good candidate for that.

I eventually will do this, but right now i am prototyping so i dont want to this now.

 

What do you mean with "globals"?

I use two global variables: One struct for accessing platform file io + main memory and one struct for storing profiling events. Thats it and this is totally fine.

Edited by Finalspace

Share this post


Link to post
Share on other sites

I meant what I said, i.e. don't use them. I don't see why you'd need it for I/O, and even for profiling, it probably wants to be a static class member rather than a global (e.g. to handle profiling data from multiple threads).

Share this post


Link to post
Share on other sites

I meant what I said, i.e. don't use them. I don't see why you'd need it for I/O, and even for profiling, it probably wants to be a static class member rather than a global (e.g. to handle profiling data from multiple threads).

 

Modifying data from multiple threads is just a matter of using proper atomic operations to ensure correct reads and writes (Memory barriers, CAS, etc.). This have nothing todo with using static or class whatsoever. Even the volatile keyword does not help you with multithreading at all - at max it puts in a memory barrier when the compiler is nice enough.

 

Btw. i tried moving the physics module in a freshly created static library... but i wouldnt compile or wont proper integrate into the main project -> So totally dropped it and wont touch it again.

 

Now i have one translation unit and every other cpp file is marked as header only, this compiles very fast and is much better to handle.

Edited by Finalspace

Share this post


Link to post
Share on other sites

Modifying data from multiple threads is just a matter of using proper atomic operations to ensure correct reads and writes (Memory barriers, CAS, etc.). This have nothing todo with using static or class whatsoever.

This reminds me of a discussion I had with a previous boss:

Me: "We should use C++ strings, because they're cleaner and safer"

Him: "No, we should stay with C strings/pointers/arrays, it's just a matter of using proper operations to modify them"

Encapsulating these accesses in one place lets you do it properly, once. Having it global is trusting yourself and everyone else who touches the code in future to do it properly every single time.

Share this post


Link to post
Share on other sites

Modifying data from multiple threads is just a matter of using proper atomic operations to ensure correct reads and writes (Memory barriers, CAS, etc.). This have nothing todo with using static or class whatsoever.

This reminds me of a discussion I had with a previous boss:
Me: "We should use C++ strings, because they're cleaner and safer"
Him: "No, we should stay with C strings/pointers/arrays, it's just a matter of using proper operations to modify them"
Encapsulating these accesses in one place lets you do it properly, once. Having it global is trusting yourself and everyone else who touches the code in future to do it properly every single time.


Yeah mean like this?:
 
class Profiler {
private:
  static ProfilerEvent _events[2][MAX_PROFILER_EVENTS];
  volatile u64 arrayIndex_eventIndex;
public:
  static void RecordEvent(EventType eventType, char *guid) {
    ...
  }
};
This type of encapsulation is actually fine, but for my game development i do, i still prefer the other way which is the total opposite when i am writing business applications.
Btw: You can do this with a struct defined as a global in the exact same way as well.

So i like this much better:
 
struct Profiler {
  ProfilerEvent _events[2][MAX_PROFILER_EVENTS];
  volatile u64 arrayIndex_eventIndex;
};

static Profiler *globalProfiler = nullptr;

static void ProfilerInit(Profiler *profiler) {
  globalProfiler = profiler;
}

static void RecordEvent(ProfilerEventType eventType, char *guid) {
  Assert(globalProfiler != nullptr);
  ...
}
When i need to share this i explicitly call ProfilerInit on the part which gets shared.

This way i totally know, when this thing is shared between both and only on a dll reload i must reset the pointer.

In the case i use a class i have no idea how this will be handled and when the memory will be allocated.
The virtual memory is not shared between the executables, isnt it? I simply dont know.



Also regarding that string example:

If i write a application which makes extreme heavy use of string manipulation, i may would use std::string but for game dev i still prefer char arrays. Edited by Finalspace

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement