Jump to content

  • Log In with Google      Sign In   
  • Create Account


Codarki

Member Since 17 Feb 2004
Offline Last Active Sep 13 2013 07:22 PM
-----

#5093865 Code organization for larger win32 projects

Posted by Codarki on 13 September 2013 - 02:35 PM

I'm finding it harder to navigate the increasingly larger main source file.

Usually my main.cpp file is just a few lines. Then there's program.cpp or application.cpp. After that it gets more domain specific, high level class what the application does. Then there might be 10 more layers all down to the lowest level classes and functions. It all depends how big the program is. You should introduce higher level abstractions whenever the program grows too big, or if you know the requirements beforehand you can try to do it prematurely (beware of overengineering).

 

I'm sure if you can give an example, many of us can give our thoughts.




#5093859 virtual destructor

Posted by Codarki on 13 September 2013 - 02:15 PM

 

You don't actually need virtual destructor if base class doesn't have any resources (or destructor defined). Omitting virtual destructor from virtual class really confuses developers though.

 

D3DX11 asynchronous loading/processing base classes have virtual functions without virtual destructors, which are meant to be inherited. You need derive them and introduce virtual destructor to make them work correctly.

Wrong conclusion. Those base classes don't need virtual destructors because they aren't freed by calling delete on a pointer to the base class, not because they have no resources in the base class.

 

Yes. But if you're making exception safe engine, you have to call the Destroy() functions automatically.. And for that you have to wrap it with virtual destructor. Sorry I wasn't specific enough in the exaple.

 

EDIT:

Only because documentation says they're COM objects.I remember that some of those wasn't really a COM object. Might remember be wrong. Haven't seen that deprecated crap in years.




#5093847 virtual destructor

Posted by Codarki on 13 September 2013 - 01:30 PM

You don't actually need virtual destructor if base class doesn't have any resources (or destructor defined). Omitting virtual destructor from virtual class really confuses developers though.

 

D3DX11 asynchronous loading/processing base classes have virtual functions without virtual destructors, which are meant to be inherited. You need derive them and introduce virtual destructor to make them work correctly.




#5007547 Best language to start programming in?

Posted by Codarki on 05 December 2012 - 04:29 PM

C++'s strength is the RAII, and it's weakness's are the C compatibility and weak library support. You need ton of knowledge to set up environment and write basic Win32 application without using some crappy GUI library (and all GUI libraries in c++ are crappy imo). Most of the internet, books and open source projects practice poor c++ programming practices. You need to be very careful not to inject your program with a crappy API. And when you decide what xml, networking, sound, and math libraries you'll use, they will be all in different coding style. Unless you wrap them (all and whole API's) all up, you're left with source code with 5 different coding conventions and workaround for the poor quirks from their poor design.

C# has extensive library, and it's all uniform syntax. Even 3rd party libraries follow the MS conventions. It also has better IDE and dev tools. It is much faster to develop at the beginning. There are many design flaws in the dotnet classes, some originating from the flaws of the language, and some just poor OO design. In the end it can get very verbose syntax wise (even more so than c++), and you have to be very careful if you want to do anything deterministically.

Java's design has been flawed from get-go, and the repairs haven't worked yet.

Overall, at high level, most concise source code will come from c++, but getting there will take lot of of code and might require you to build "meta language" with lower level classes first.

Now, after being out of picture for a long time for c++, Microsoft seems to invest heavily in (modern) c++ now. Maybe they will try to create some decent library for the first time ever. There's even some hints that they might try and make native compiler for C#.


#5001628 Completed Tic Tac Toe - Critical Critiques and Advice

Posted by Codarki on 16 November 2012 - 02:31 PM

First, that is pretty well done, very easy to read.

I noticed this:

I see main()'s board getting passed into a lot of member functions. Why not make it a member variable?

Which brings me to the issue that this whole program has only static methods. So the suggestion of adding a member variable wont work.

How about trying to create a class called Board and instantiate it? It wont make the source code shorter, but it will focus some code to specific place. It will increase the so called cohesion (http://en.wikipedia.org/wiki/Cohesion_%28computer_science%29) for different elements (or compononents/modules/concepts) of your program. Basically most of you operations of the board data would be abstracted away there.

The Board class could have methods such as:
Clear()
Draw()
IsElementFree() (SpaceFree)
MakeMove()
IsFull()

Also, you can add loops inside of your CheckWinner() method.


#5001191 What's your take on protected variables?

Posted by Codarki on 15 November 2012 - 05:08 AM

I think its useful when your intending on creating a derived class that is derived from multiple parent classes.

Can you elaborate on that?

Does that read as: it's useful to have protected members in parent classes when using multiple inheritance?


IMO if you're in the unfortunate situation that you must use multiple inheritance, don't add more confusion to the mess with protected members.


#4989504 How to allocate objects

Posted by Codarki on 12 October 2012 - 10:09 AM

  • M allocates (using new) a pointer to the O instance. It has to be deleted with the destruction of M.
  • M uses std::unique_ptr<> to the O instance. A call to "new" is still needed, but destruction will be automated.
  • The O instance is declared as a member of M.
I suppose alternative 2 is usually to prefer before alternative 1. But the choice between 2 and 3 is harder.

Instinctively, I prefer option 3. That way, I don't need to bother with dynamic allocation. But the drawback is that the declaration of O has to go into the header file of M (whereas only a forward declaration is needed for alternative 1 and 2). If there is a complex data hierarchy, it can all end up (indirectly) with a lot of includes in the top-most type. And that does not "feel right".


1. You must either disable or implement copy and move semantics for M.
2. I usually go with this initially.
3. If M is implemented using pimpl, then header include is not needed. O instance can be a member of implementation of M. (efficient as 1 and 2, plus other benefits)

I usually go with plain members for low-level classes, pimpl for high-level classes, and unique_ptr<> for all others requiring heap allocation.

I usually follow the pattern to have a trivial constructor that only makes sure everything is cleared, and then a Init-function that do the real initiation (which can also return a failure).

I usually follow the pattern to have a trivial constructor that only initializes members, and separate the building of the object outside of the class (to either builder or free function). The building process can fail returning empty or throwing exception, or by using builder object you can pass it around, and create more elaborate construction error reporting. IMO this has many other advantages, such as simplicity and reduced compilation times.


#4982519 Some questions about multithreading

Posted by Codarki on 21 September 2012 - 04:05 PM

I've read you post couple of times and it's a bit incoherent. Could you elaborate a lot, like what you've done, or planning to do?

IMO abstracted scene graphs are not practical. It is much more easy to have multiple typed object spatial trees.

Lets talk about 4 core CPU on D3D11. Multithreaded 3d graphics takes a lot of discipline. First, you only make so many threads the current CPU has cores, the worker threads. You dont want context switches. Some physics and audio libraries spawn own threads, so you end up with more threads than cores in the process. Then you build the engine on tasks. Tasks will have dependecies to other tasks. Update task must become before frustum culling task for lights, before shadow rendering task, which must become before lighting pass where the shadow is used. But you can render G-buffer simultaneusly, just after the update task. There will be a task scheduler, which feeds the taks to queues. Main thread will create the window, schedule tasks and add couple synchronization points.

It's efficient to have 4 task queues, one per thread, consumers (worker threads) does not need locking for fetching tasks. If the dependencies are correct, there is no need for synchronization (mutexes or lockfree) for any data access. When the queues are populated with tasks for the frame, main thread can join work (you only need 3 threads plus main). Most important thing is to make your codebase more functional, and all the shared data must be made immutable. I assure you, you can make 32 core multithreaded d3d11 graphics engine with 0 mutexes, no lock-free code (lock-free will trash all cache), which keeps all cores fully utilized.

Ofcourse I'm only talking about using deferred ID3D11DeviceContext. It just emulates the multithreading for commands you feed it, you could very well create own mechanism for it. The graphics driver will play back all the commands sequantially anyway. (though the driver has like 3 frames worth of commands to keep GPU busy, say thousands of draw calls queued up)

But one doesnt always need to multithread. If you spam the D3D API with single thread, some graphics drivers are multithreaded, and they will spread the load (assuming one can push the bottleneck/load to the driver).


#4981701 Finalizers confusion

Posted by Codarki on 19 September 2012 - 08:52 AM

But why to require 2 passes? Cannot we just run the finalizer immediately before releasing a blob?
Perhaps this is consequence of having separated trace phases and a collection phases? Or maybe it's something dealing with multiple threads?

My guess is that it's becouse multiple threads. Another thread can reclaim the memory, while another thread is running the finalizers.

See Finalization Internals at http://msdn.microsoft.com/en-us/magazine/bb985010.aspx

Anyone knows how KeepAlive helps with the problem? I'm inclined to speculate it might actually be NOP just to prevent the flow analyzer from marking the reference unused.

I guess the detection for unreferenced objects happens at bytecode level, and the scope for an object is not as it's seen in C# code. So maybe KeepAlive just keeps the object referenced, as it's seen in CIL and bytecode.


#4973757 A design pattern question

Posted by Codarki on 27 August 2012 - 06:16 AM

Yea you're using singleton pattern, and seeing some problems what they can cause. If you create the systems normally and give global access to them, you can control the construction and destruction orders.

// AssetManager.h file
class AssetManager {};
// Global accessor.
AssetManager& DefaultAssetManager();
void SetDefaultAssetManager(AssetManager* manager);

// AssetManager.cpp
namespace {
	AssetManager* g_DefaultAssetManager = 0;
}
AssetManager& DefaultAssetManager()
{
	assert(g_DefaultAssetManager);
	return *g_DefaultAssetManager;
}
void SetDefaultAssetManager(AssetManager* manager)
{
	g_DefaultAssetManager = manager;
}

// main.cpp
#include "AssetManager.h"
int main()
{
	{
		// Here you manage the lifetime of these objects, aswell construction
		// and destruction order.
		AssetManager manager;
		SetDefaultAssetManager(manager);
		OtherSystem system(manager);
	}
	SetDefaultAssetManager(nullptr);
}



#4973209 Self-storing class-instances on creation

Posted by Codarki on 25 August 2012 - 12:28 AM

- Singleton and having the objects register themselfs (I don't really see any downside in my current design plan with this)
- A factory class that registers objects on creation
- Having to register objects manually


I'll add one which I believe is more modern style RAII approach compared to other suggestions here:
- Objects register themselves to a registry, which is passed in to their constructors. And unregisters at destruction. No static objects nor static functions required.


#4972983 Friend and adress function

Posted by Codarki on 24 August 2012 - 08:08 AM

The friend declaration says that only that function is a friend. The functions declaration is:
TextureMgr& GetTextureMgr();

Friending the function gives the function an access to its private constructors, since it can't be instantiated otherwise, and returns a reference to static TextureMgr object.


#4961451 Interfaces vs Abstract Classes as a module boundary.

Posted by Codarki on 20 July 2012 - 02:29 PM

Personally, I prefer interfaces with some kind of (optionally) abstract default implementation added to the mix. I don't consider base functionality to be "clutter", since - provided said functionality is carefully chosen - it greatly helps with code reuse and avoids code duplication,

In summary I prefer interfaces to define the basic contracts along with (abstract) base classes that provide a basic implementation.

I'd argue that the shared base implementation does not need to be part of the interface, nor class hierarchy, but can be moved to a helper class which is composited in the actual implementation. And the code can be shared, and IMO allows faster response to future changes. The only downside is that there's some boilerplate for each class to forward the calls to the base implementation.


#4961444 Entity VS Behaviour

Posted by Codarki on 20 July 2012 - 02:13 PM

if you put logic and data into one component the logic inside the component can only use the data which is also inside the component. That means you have to implement a message system to send messages between components or something similar. And there will be a lot of messages.


But what if you make the components self sustained, so they don't have to access data from other components? This can of course mean moving of more data.

Or do you mean the order of updates to data in components depends to data in other components? You'd have to order the updates anyway, either in logic in system, or by building ordered update of components so no stale data is accessed.

Also, surely a system only access the components in it's own domain, and passes messages to other systems?

The way I see it, you don't want components to communicate to each others, becouse that would be logistic nightmare. And you don't want to put all component's logic in one system, becouse you'd have one system per component type (or one massive system per multiple component types). Maybe some kind of subsystem, recurse the general idea.


#4961395 Deleting from vector with iterator (99% sure this is the way to go)

Posted by Codarki on 20 July 2012 - 12:21 PM

Above solutions should all work.. but if order doesn't matter and extra speed is warranted:
// If order doesn't matter, swap to back and and pop. This wont move the
// elements after current iterator during erase, but the order of elements
// will change.
for (vector<Enemy*>::iterator it = enemyVector.begin(); it != enemyVector.end(); )
{
    if ((*it)->getState())
	{
		delete *it;
		// Swap to back and pop, and delete it from there, but don't swap the last element.
		if (enemyVector.size() > 1)
			std::swap(*it, enemyVector.back());
		enemyVector.pop_back();
	}
	else
	{
		++it;
	}
}


Edit: Disregard that, I'll edit another solution (different from above)
Edit2: there.




PARTNERS