Jump to content

  • Log In with Google      Sign In   
  • Create Account

Awesome job so far everyone! Please give us your feedback on how our article efforts are going. We still need more finished articles for our May contest theme: Remake the Classics

Codarki

Member Since 17 Feb 2004
Offline Last Active Jan 18 2013 11:41 AM
-----

#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.


#4961248 How to I pass a variable over to this?

Posted by Codarki on 20 July 2012 - 03:38 AM

The documentation says that 2nd parameter, the void pointer, is application defined value passed to the enum callback.

Try passing a pointer to a struct when you call the enum function, something like:

struct JoysticEnumParams
{
IDirectInputDevice8* context;
IDirectInput8 *DInput;
};



#4960876 Does any interest in the Go language still exist?

Posted by Codarki on 19 July 2012 - 04:23 AM

... operator overloading is a nightmare when reading code, even more than macros:

In general, I have to admit that I’m a little bit scared of language features that hide things. When you see the code
i = j * 5;

… in C you know, at least, that j is being multiplied by five and the results stored in i.
But if you see that same snippet of code in C++, you don’t know anything. Nothing.


If i = j * 5 does something unexpected in C++, that programmer should be fired.

The only way to know what’s really happening in C++ is to find out what types i and j are, something which might be declared somewhere altogether else. That’s because j might be of a type that has operator* overloaded and it does something terribly witty when you try to multiply it. And i might be of a type that has operator= overloaded, and the types might not be compatible so an automatic type coercion function might end up being called

The types are int. Most likely the type information is right next to the expression. If the type of j is not int, and you want to multiply it with 5, then you overload an operator. If the type of i is not int, and it must be assignable from int (or whatever type the j is), then assignment operator must be overloaded. Also, implicit conversions are very commonly avoided.

Now whatever 'witty' C++ example you can imagine (which doesn't do something unexpected, like logging the result over a netword), try to think how you would do the same functionality without operator overloading.

And the only way to find out is not only to check the type of the variables, but to find the code that implements that type, and God help you if there’s inheritance somewhere, because now you have to traipse all the way up the class hierarchy all by yourself trying to find where that code really is, and if there’s polymorphism somewhere, you’re really in trouble because it’s not enough to know what type i and j are declared, you have to know what type they are right now, which might involve inspecting an arbitrary amount of code and you can never really be sure if you’ve looked everywhere thanks to the halting problem (phew!).

Public interface for the types should be enough, no need to check the implementation code.
I'd say if there is inheritance, and overloaded operators for derived and base classes, the design might be shaky.
If you need to know the real derived type of polymorphic variable, you are violating liskov substitution principle and abusing inheritance.

Sure, you can abuse everything in C++ and say 'see the language is bad', you can abuse regular functions by naming them all 'Fred_X' with a number. IMO that doesn't warrant a language that keeps you in safe-jacket. Bad programmers should be fired, and nobody (hopefully) does intentionally cryptic code.

Usually overloaded operators contain the most simple and bugfree code. Using them provides very simple syntax. If they are the cause of unexpected effects or source of bugs, then whoever wrote them is at blame (and should be fired).


#4960861 Is using std::move() necessary?

Posted by Codarki on 19 July 2012 - 03:24 AM

Not only never necessary, but a bad idea on function return (it may inhibit copy elision / RVO)

Seems like you're right.

See also "Moving from lvalues" and "Moving out of functions" here:
http://stackoverflow...11540204/859774

Very good explanation.

return C;
return Matrix©;
These should call the copy constructor (in debug build with all optimizations off)


Ah this was wrong. C++ standard has special rule:


When the criteria for elision of a copy operation are met or would be met save for the fact that the source object is a function parameter, and the object to be copied is designated by an lvalue, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue. If overload resolution fails, or if the type of the first parameter of the selected constructor is not an rvalue reference to the object’s type (possibly cv-qualified), overload resolution is performed again, considering the object as an lvalue. [ Note: This two-stage overload resolution must be performed regardless of whether copy elision will occur. It determines the constructor to be called if elision is not performed, and the selected constructor must be accessible even if the call is elided.


This means it will first try to move, and then copy. I wonder if copy constructor has side effects, it might bite someone.




PARTNERS