Jump to content

  • Log In with Google      Sign In   
  • Create Account

Banner advertising on our site currently available from just $5!


1. Learn about the promo. 2. Sign up for GDNet+. 3. Set up your advert!


Trienco

Member Since 22 Aug 2001
Offline Last Active Yesterday, 10:02 PM

#5062193 c++ include define DIR

Posted by Trienco on 15 May 2013 - 10:03 PM

As mentioned above, there's a whole bunch of methods that are all better than abusing the preprocessor for this.

 

a) If stuff is versioned, why not use a version control system instead of folder names?

b) Failing that, the -I option exists for exactly that kind of thing (ie. it belongs in your project settings or Makefile, not the actual source code)

c) Use a symlink that points to whatever version you want to use (yes, Windows finally has them too)




#5062192 My ever-evolving coding style

Posted by Trienco on 15 May 2013 - 09:51 PM

Whenever writing a million getters and setters gets annoying, I usually ask myself two things:

 

a) if I just go and write trivial set/get methods for everything, why not just stop pretending and make it public?

 

b) why not be lazy and wrap it in a template that handles it?

 

class ...

{

...

   Property<int> stuff;

};

 

int value = obj.stuff();

obj.stuff(5);

obj.stuff( obj.stuff() + 5 );

 

Not calling it get and set allows to use the template for all trivial cases and do custom implementations where needed without requiring a different syntax.

 

Still, if a class has a dozen members and trivial accessors for all of them, encapsulation probably just went out the window. So it's more of a convenient way to remain consistent.




#5061418 tictac toe marks

Posted by Trienco on 12 May 2013 - 10:09 PM

Generally by giving them names and making them constants, but that only applies to numbers that _must_ be defined in the program and won't change at runtime. The resolution or window size for a PC application should not be a magic number or a constant, because you don't know what hardware people are using.

 

In your case: by learning how to use structs, arrays, loops and functions.

 

Just doing a find/replace and creating thousands of constants like "block1TopLeftX" and "block3BottomRightY" might get rid of magic numbers, but is also insanely silly, because a sane person would never want to hard code everything for every single block in every single level of a game like Breakout (which will be thousands or even a lot more). Always tell yourself "there will probably be a bug in this code, I don't want to fix the same bug in 10000 copies of this". In fact, writing the same snippet more than twice (at most) is usually a bad sign.

 

Force yourself to keep things flexible. Plan for change, write code that will work no matter if your game board is 3x3, 5x5 or any other size (somewhat limited by screen size) and don't assume you will always need 3-in-a-row. Make board and required row size a variable and don't use magic numbers or constants. Remove the option to just hard code everything and force yourself to think in algorithms instead of a huge list of if-elses.




#5060761 tictac toe marks

Posted by Trienco on 09 May 2013 - 10:50 PM

Sorry, I don't see any of these attempts of an "easy way out" working. Looking at this code and the breakout code, no amount of switching language, UML drawings or doing apps instead of games will allow avoiding to take a huge step back and learn some basic and fundamental programming first. That means understanding and knowing how and when to apply them, not "research" as in "I skimmed over a tutorial and compiled the code".

 

The code is an insane mess of copy/paste, arrays are used, but then stuff is just copied and hard coded for every field in the array, showing a complete lack of understanding WHY an array is used in the first place. 

 

Data that should be grouped in a class or struct is instead spread over a bunch of different arrays, functions are just doing all kinds of stuff. 

 

There's endless if/else-copy/paste replacing a simple division. 

 

Despite making the same mistake in the breakout thread, this function is again presenting an entire frame when it should just set a value (so I doubt that it was even understood what those magical D3D functions are actually doing). 

 

There is also no recognizable understanding of the difference between game logic and graphics. Graphical representation isn't the game, it's what you put ON TOP of the game, so a player can see what he's doing. The "core" of the game doesn't care about graphics, sprites, 2D/3D, keyboards or mice. Inputs are used to trigger functions of the game and graphics are used to display the state of the game.

 

You can't buy a Spanish dictionary, learn how to pronounce two or three words and then try to write an entire novel. You need to learn the grammar and rules, have a decent vocabulary and know common phrases and idioms.

 

In this case: you need to learn programming. The language here isn't C++ (or C#, Basic or any other specific language), it's basic programming concepts. Not just knowing what a loop looks like, but how to use them. Forget about GUI and 3D (or even 2D). Running requires knowing how to walk first. Otherwise you're not getting far and keep hurting yourself.

 

Planning for TicTacToe or a calculator by using ULM and flow charts is overkill. Maybe a nice practice, but if you need a flow chart to understand the game logic behind TicTacToe (of which 95% should be covered by a standard game loop), you are clearly overcomplicating a very simple thing. Frankly, at this point it seems more like avoiding the real issue rather than tackling it. Like constantly "taking a break from programming" it won't magically make you write better code.




#5059082 Code Review: Pong

Posted by Trienco on 03 May 2013 - 10:21 PM

One book had a very nice way to define comments in code. "Comments are the documented failure of a programmer to write clean and readable code".

 

That doesn't mean that some comments aren't justified. Explaining _why_ something is done in a way that seems weird is often useful ("working around a bug in compiler x", "doing it like this for performance reasons"). Mentioning what the code does on a _higher level_ ("using optimized algorithm from <some link>").

 

Avoid comments where possible, because most of the time they are outdated by the time you finish typing them, will be confusing more than helping after two weeks and turn into flat out nonsense another few weeks later. Unless your team exists of super disciplined programmers that will always also maintain the comments, no matter how closely that deadline is looming above them.

 

I'm also not a fan of aligning your assignments. It's not about looking pretty or creating an excel sheet. If stuff is indented so far that you need to bring a ruler to find out which value belongs where, then things just got counter productive. I find it much more useful to insert a blank line to separate blocks that logically belong together.

 

Besides, having big blocks of assignments is often indicating a different problem. Not getting over the C habit of declaring everything at the start of a function.

 

For reset or init of simple classes (mostly data, nothing fancy in constructor/destructor), it can often be more convenient to just assign a default constructed object ("object = Object()"). It's also a lot more robust, since you can't forget variables, don't have to touch multiple sections of code when adding new variables and it's immediately obvious to someone reading the code.




#5056554 isNearZero( Vector)

Posted by Trienco on 24 April 2013 - 10:21 PM

If it's just for assertions, why pepper the code with a ton of if's, instead of just doing "return dot(v,v) < eps"? I'd imagine a dot product and one comparison to be not just shorter and easier to read, but also a good bit faster. Only thing to keep in mind is that this returns the squared length, but that shouldn't really matter much.




#5056266 Multiple types in a map container C++

Posted by Trienco on 23 April 2013 - 10:34 PM

I'm confused. You say it's _exactly_ 9 slots and they are _consecutively_ numbered 0-8? At which point did that make you think "map<int, ptr>" instead of "array<ptr, 9>"?

 

Why fill it with dummy instances and loop through all the pointers and call functions on them, instead of just checking them for 0?

 

And why even do that, instead of just adding a simple "numItems" variable to box that keeps track of the next free slot?

 

 

class Box
{
public:
    Box() : numItems(0) {}
 
    void addItem(shared_ptr<Item> item)
    {
         if (numItems < 9)
         {
              items[numItems++] = item;
         }
    }
 
private:
   std::array<shared_ptr<Type>, 9>;
};

 

Why pass a shared_ptr? Because in your code you never check if the item was successfully added. You ownership is completely unknown at this point. If you delete it at the calling site and it was added -> access violation. If you don't delete it and it wasn't added -> memory leak.

 

Why a shared_ptr? Without knowing the usage of that box, it is safer to allow code that is currently using an item from the box to keep it alive, just in case it gets removed from the box in between. Using a smart pointer also means there is no need to explicitly delete an object in the box before replacing it (if that is even a use case).

 

 

Your main function (as explained above) also has a big issue.

 

 

int main()
{
     Box myBox; //This has no business being a pointer in the first place
     myBox.addItem(make_shared<ItemA>("Steve", 0, "is a man", 4, 6, 7, 17, 8, 3.4f);
}

 

 

The real problem is of course trying to stuff two completely unrelated types into the same container. Why? It will just result in a huge mess of if/else and dynamic_casts or getType(). Put them in two different arrays and let the box class hide that fact. Of course if you ever want to access an item in the box, you're back to the same problem. What type should that function return?

 

Generally you want to hide all members and use a _common_ interface for A and B. Depending on what they are supposed to do, try to abstract that. You want them to print their data? Have a virtual function "print" in both of them (and the base class). Don't even think about placing that code outside the classes and have it rip the data from their guts.




#5053330 Is this overkill? Potentially error-prone?

Posted by Trienco on 14 April 2013 - 09:54 PM

This should not be. The data and functionality should be only related to the character, nothing more. I might be reading the 'functionality and data related...' part to literally though. What I gather from this is you have functionality in the character class that operates on things that are not the character. If so, then you need to break this into another function that takes the character and the thing it is acting on into a completely different function.

 

Should have been more specific, This isn't what I meant.  I just meant that a character can do a whole lot of things, the current character class contains methods that do things like attack, buying and selling items, moving from one place to another on the world map, moving from one tile to another on the battle map, talking to an npc, etc.  My meaning was that my characters have a lot of functionality that, while certainly should be owned by the Character class, is often dependent on very distinct parts of the game.  But the class is BIG, and that's ultimately the problem I'm trying to solve.

 

Those are actually very good examples of things that should probably not be in the character class. Generally you want to minimize your dependency on other classes and modules, doing everything at such a specific level is achieving the opposite. Why does your character need more than functions to add/remove gold and add/remove items? The whole process of buying or selling definitely does not need to be in the character class.

 

Moving around the world? If you just talk about changing position, then the position should be set from the outside. What if you want other objects to move around that aren't characters? Will you copy/paste the whole code? And if it includes path finding, that is again a generic and reusable thing that doesn't belong in a character class.

 

A conversation is also a thing of its own and while it might need references to two characters, I don't see why it should all be handled in a character class. Not everything that involves a character should be dumped in there. Especially in C++ one should seriously reconsider the urge to put every function into a class in the first place. Global functions that manipulate objects through their interface are not only "acceptable", but the best way to be modular and extendable, as you can add a whole ton of functionality without touching existing code (buyItem(Character& character, Shop& shop)).

 

Everytime you ask yourself "should this be part of class X or class Y, because it involves both of them" you should consider that the answer might be "neither".




#5050204 Why can I call a nonconst function with a const object?

Posted by Trienco on 04 April 2013 - 10:07 PM

Because temporaries returned from functions are r-values and considered const. You are not supposed/allowed to modify a temporary variable.

 

You are also wrong about the lifetime. The lifetime of a temporary doesn't extend to the end of the surrounding block. It ends with the expression it is used in. One big and good to know exception is having a const reference to it, which will keep the temporary alive for as long as the reference exists.

 

doSomething(createTemporary());
//temporary dies here
 
{
     const Temp& temp = createTemporary();
     //do stuff
}
//temporary dies here, because reference goes out of scope



#5044788 Am I a bad programmer?

Posted by Trienco on 19 March 2013 - 11:45 PM

Finally, if you launch an application in Visual studio with CTRL + F5 ( I believe thats the key combo ), it will automatically prompt for a keypress before closing the console window.

 

That shortcut is extremely useful, but it's important to remember that it means "run without debugging". You can't step through your code or hit breakpoints. Of course that shouldn't be an issue, because if you ARE debugging, just set a breakpoint at the end of main. Also, it only works if you configure the project to "system" console (if you created an empty project, you have to do that manually).

 

Writing a good tutorial has two requirements. You don't just need a good grasp of the subject at hand, but you also need the ability to teach and explain. We had plenty of tutors at university who really knew their stuff, but absolutely sucked at conveying that knowledge. So the tutors where happy to get their money and the students stopped showing up, because it was a waste of time.

 

So essentially the number of people qualified and willing to write a really good tutorial is rather limited.

 

In terms of diary, I remember doing that when I felt like rewriting JA2. I gave up after 5 days, because I spent more time writing than programming. Of course if you write about it, you get ideas or notice mistakes, so you always constantly change between coding and blabbering. Technically a good thing for your code, but also very time consuming. Looking at it now, it just makes me cringe and go "why didn't you just use TBB and boost instead of rolling your own?" However, it had the advantage of not hurting anyone. Not only because nobody ever read it, but because it never had the intention of "teaching how it's done".

 

I'd still suggest doing that instead, because explaining why you do something forces you to think about it, which will typically lead to research and understanding.

 

While I'd like to think I'm good enough at C++, I still wouldn't write tutorials, because I suck at explaining things in a concise way while still being "complete". Also, there already are a million books and tutorials about all the basic stuff, so I would just add to the noise. Maybe if I ever come up with something that I feel is "new" or important enough to be shared.

 

But heads up, the price for worst C++ tutorial ever goes to whoever wrote that the "int" in "int main" means "start program execution here" and that the "return 0" at the end doesn't really mean anything, but just has to be there. That looked like somebody just read the back cover of "C++ for Dummies" and felt like writing a tutorial about all the things that mystified him.




#5043254 Way to pre-declare standard templated class types?

Posted by Trienco on 14 March 2013 - 11:11 PM

Usually precompiled headers are brought up a lot with template heavy code to reduce compile times. Is that not an option or are build times still too long?




#5030299 New c++11 video tutorials, tell me what you think.

Posted by Trienco on 08 February 2013 - 11:56 PM

After having seen stuff like "MyClass" and "myInt" in actual production code (map<...> theMap), I don't think it matters what he uses. If the variable has absolutely no meaning besides being there to demonstrate its existence, the name will ultimately be pointless and people will copy that, no matter what. At least one person would probably even copy it if it was named purelyForDemonstrationAndForGodsSakeDontNameYourVariablesLikeThis.




#5029301 Rule of Three, and const-correctness questions

Posted by Trienco on 05 February 2013 - 11:26 PM

Since const correctness still comes across as something that's "nice but not mandatory", let's look at things that will fail if you don't write const correct code.

 

First, the one thing that can really get me mad: people writing interfaces/APIs using char* instead of const char*.

void parseString(char* str) { ... }
 
std::string text;
parseString(text.c_str()); //Fail, as c_str() returns const char*

 

 

While these have a simple workaround, it can still be annoying

void analyseObject(MyClass& obj) { ... }
 
analyseObject( MyClass(a,b,c) ); //Fail, temporary variables are r-values and const

 

struct MyClass {
   void print() {...}
}
 
MyClass(a,b,c).print(); //Fail, temporary = const

 

 

To clarify the observable effects of ignoring the Rule of Three, because unlike missing const correctness, this won't conveniently fail to compile in the first place:

 

struct MyClass
{
    int* data;
    MyClass() : data(new int) {}
    ~MyClass() { delete data; }
};
 
 
vector<MyClass> v;
v.push_back(MyClass()); //Fuse is lit
cout << v.front().data; //Fail
v.clear(); //Fail
 
void function(MyClass a) { ... }
 

 

Let's ignore compilers optimizing out the temp variable or using move semantics, since you can't rely on it.

 

First example creates a temporary instance, then creates a copy in the vector (which just stupidly copies the pointer). The temporary goes out of scope and deletes data. The next line accesses data that was just deleted. You will see either garbage (kind of helpful), crash with an access violation (very helpful) or it will just seem to "work fine" (dangerous), as the memory will most likely not have been overwritten yet.

 

Then we clear the vector and delete the instance in it. Now data is deleted a second time. If you are lucky, your application will crash.

 

 

The second example looks harmless, but does pretty much the same. To call the function, you create a copy. When the function returns, this copy is destroyed and deletes your data. The original object is now in an invalid state with data pointing to deleted data. Any access will hopefully crash and when the original object goes out of scope, the memory is attempted to be deleted a second time.

 

 

Why is crashing the best case scenario? Because it is hard to miss and clearly tells you there is a bug. The same bug could result in working "just fine" most of the time, but returning strange values on every odd Thursday during full moon. This can cost you days or weeks just trying to reproduce the bug and tracking it down.




#5028540 First person camera and matrix transforms

Posted by Trienco on 03 February 2013 - 11:02 PM

You already are doing the inversion in the last block of your lookAt function, when you transpose the rotation part and invert the translation part. This method only works for special cases, but unless you "break" things by adding scaling to your matrix, you have that special case.




#5028277 Timing seconds per frame in C++

Posted by Trienco on 03 February 2013 - 02:13 AM

Using microseconds gives even more accuracy, however there are not any standard microsecond timers in C++, so you have to use third party or OS-specific timers, unless you are using the new C++11 standard's <chrono> library, which has a high precision timer that might be microsecond or better, but falls back to milliseconds if microsecond precision isn't available.

 

If you are using VS 2012, you should use the boost implementation of the chrono library (unless they updated it in the meantime). Why? I basically picture the conversation like this:

 

Marketing: "We already put chrono support on the website."

Devs: "But we can't make it in time!"

Marketing: "We don't give a damn."

Devs: "Let's base all clocks on the standard timer, pretend we implemented it and just deal with the bug reports later."

Marketing: "See, was that so hard?"

 

I'd definitely use some chrono implementation, though. It's the new standard, it's platform independent and you don't have to worry about the potential pitfalls of using QueryPerformanceCounter on multi-core CPUs.






PARTNERS