Jump to content

  • Log In with Google      Sign In   
  • Create Account


We have 4 x Pro Licences (valued at $59 each) for 2d modular animation software Spriter to give away in this Thursday's GDNet Direct email newsletter.

Read more in this forum topic or make sure you're signed up (from the right-hand sidebar on the homepage) and read Thursday's newsletter to get in the running!


Member Since 09 Dec 2005
Offline Last Active Today, 08:04 AM

#5200061 Getting Rid of "char*"

Posted by Bregma on Today, 08:05 AM

Like Ryan_001 said, you'll either need to provide an interface for your family of String classes and program to that interface, or provide a family of function overloads for each member of your String class.  I'd recommend the interface method, since you can't overload on a return type so you'll need to explicitly instantiate such templated function overloads at the call point instead of relying on deduction, and that can quickly make you code messier.


Don't worry about all the bloat that using a separate class for every possible string length will introduce. Modern computers have more than enough memory and disk space to handle that and statistically speaking you're unlikely to have more than a few hundred such classes (or function overloads).  If it turns out all the code fragmentation blows your cache coherency noticably, get a bigger i7 with more cache.

#5199691 Converting my program to smart pointer and a bit confused

Posted by Bregma on 23 December 2014 - 06:47 AM

(1) You should always prefer references over pointers, so converting existing code that uses references to use pointers is a step backwards.  If an API requires pointers to be passed in (and many of them do), just pass in pointers to your object (or pointer to your object references).  If an API passed back pointers (ie. controls object lifetimes), treat them as if they were new/delete or open/close (see below).


(2) Your first step in using smart pointers is to locate any new/delete (or open/close, or create/release) pairs and replace them with std::unique_ptr and then use that class's get() function to obtain the raw pointer, and continue to use the raw pointer where you did before.  You can, for example, cache all your textures in a vector of unique_ptrs and dole them out to consumers without them being any the wiser.


If your new and delete are not in symmetric pairs and the lifetime control of dynamically allocated objects is not straightforward, your design is broken and needs to be fixed first.


(3) Once in a rare blue moon you will run into situations in which std::unique_ptr is not going to do the trick.  That's when std::shared_ptr (and its cracker cousin std::weak_ptr) come in to play.  If it's the tool you need, reach for it, but it should not be your tool of first choice.

#5199456 Linking a static library to my static library

Posted by Bregma on 21 December 2014 - 06:30 PM

Problem is, a "static library" is an archive file of compiled objects, not a dynamic library.  It does not carry any sort of list of other transitive entities that will be used to satisfy its undefined references during the final link phase, because it has not gone through the final link phase.  If every separate translation unit had to have all its undefined references satisfied at compile time it would not really be possible to develop very useful software using the separate complication model.


It's not that it's frowned on to have static archives contain links to satisfy transitive dependencies, it's that static archives simply do not can not carry links to satisfy transitive dependencies.

#5199107 C++ how to declare something that isn't declared?!?

Posted by Bregma on 19 December 2014 - 09:55 AM

Can anyone explain why this would be useful?

Can any one explain the difference between these two lines?

  TSubparticle::Ptr p1, p2;
  TSubparticle* p3, p4;

#5198946 C++ how to declare something that isn't declared?!?

Posted by Bregma on 18 December 2014 - 09:23 AM

You're using C++ so write C++ code instead of C.

struct TSubparticle

  using Ptr = TSubparticle*;

  Ptr next_;

TSubparticle::Ptr particle_list = nullptr;

#5197771 is there an easy way to do this?

Posted by Bregma on 12 December 2014 - 08:00 AM

Because all your changes are context-specific, I doubt there is any way you can just do a mechanical search-and-replace operation.  Some times doing the hard work just requires doing hard work.

#5195792 Crash with <unable to read memory> error

Posted by Bregma on 01 December 2014 - 04:54 PM

std::unique_ptr<GameMap> Map = std::make_unique<GameMap>();
Map->CreateMap(5, 5);
It's almost 2015. Let's start teaching novice users to do things right instead of perpetrating fragile C/C++98-isms.


Not everyone has a C++14 compliant compiler (which is required for std::make_unique<>()), the standard was only ratified this last August.  Heck, plenty of folks are still stuck with a 1997-vintage compiler.


That said, if you have a C++14 compiler, that's a good way to do pointers once you understand them and the implications of ownership transfer and move vs. copy.

#5195724 Crash with <unable to read memory> error

Posted by Bregma on 01 December 2014 - 10:00 AM

It crashes because dereferencing an uninitialized variable is undefined behaviour.


Lesson number 1 learned.

#5195723 NULL vs nullptr

Posted by Bregma on 01 December 2014 - 09:58 AM

I use nullptr everywhere because this is 2014 AND WE DON'T NEED TO SHOUT AT THE COMPILER TO GET IT WORK any more.  Most of the time.  I admit that sometimes it's still justified.

#5195489 Unity or C++?

Posted by Bregma on 30 November 2014 - 09:50 AM

Option 1



(1) choose a tool for making a simple game

(2) make the game

(3) finish the game

(4) oh, wait, I learned a whole lot of stuff required to make a game and my friends stop talking when they play it

(5) rinse and repeat, solving increasingly more complex problems in increasing more complex games that you finish

(6) be an expert game developer (which includes programming, designing, maintaining, promoting, etc)


Option 2


(1) play with some tool

(b) switch to a different tool

(d) monkey around with tools

(5) dabble at starting to make several games

(6) browse the internet (heh heh, dem kittehz, oh look if I were a haircut I'd be a mullet!! lulz)

(4) go make coffee and message your friends about how lame they are for using some arbitrary tool for finishing their games

(6) play the games your friends have finished


Choose one of the above options.  I, personally, would recommend the first one but the second is very popular.

#5193660 Need a short name to replace a really really long function's name

Posted by Bregma on 19 November 2014 - 02:05 PM


I think long function names are generally a good thing as they are more descriptive. As long as you have some sort of autocomplete, you shouldn't have to type it all out, and most nowadays support just typing the initials (SPBOCP). If you are looking to reduce the length of the names, think about what information is implied by the function parameters and return type.


If I have a function

public List<Friend> GetListOfFriendsByID (int id)
    //Return new list with my friends

I can see that it clearly returns a List and I can clearly see that I am passing in an id. I can now shorten the name to

public List<Friend> GetFriends (int id)

Clearly you are getting data.  It should really just be public List<Friend> Friends(int id);

#5193357 libvorbis, libogg and libtheora

Posted by Bregma on 17 November 2014 - 08:34 PM

Yes.  Mostly.


Most AV files are containers with encoded audio and video.  A typical playback application pipeline consists of a transport stage (read or stream the data from source), parse, demux, decode, convert, and then feed to the sink.  Recording applications go the other way through a muxer.  Transcoding applications do both.


It's possible to use vorbis encoding with other container formats.  For example, a Matroska file could carry a vorbis video payload.  Ogg can also contain other data, for example text streams, as well as other formats such as AVI video.


File extensions are irrelevant unless you're trying to write a trojan virus.  Like most data files, a sequence of 'magic' bytes at the start of the file identify the actual container format, and the container indicates what data formats it contains.  Consider the advantage of magic bytes in data streaming over a network connection:  where do you put the file extension?

#5191085 Header for Common Includes

Posted by Bregma on 04 November 2014 - 05:52 AM

I'm starting to realize that this is bad practice, however. Should I only include these common files when I need to in the source files (possibly header files) where needed? Is this a better way to go?



By minimizing physical coupling, not only does it improve compilations times (even if you use precompiled headers), it helps enforce reduced logical coupling and strong layering. Those two design philosophies that can have a sharp impact on maintainability of anything larger than a trivial codebase.

#5191083 Datatype Size and Struct Compiler Padding

Posted by Bregma on 04 November 2014 - 05:44 AM

It's not just C variable size and struct padding you need to worry about:  there's also alignment requirements.
For example, if you have an odd number of chars in a string followed by a long integer, you might end up with the integer written to an odd memory address.  Some silicon bent on supporting ancient software (eg. x86) will simply be slow when accessing an odd address, some (eg. ARM) will raise a bus error and your program will crash.  Sometimes the exact behaviour depends on how the OS was configured at build time and sometimes on how the language runtime was configured at build time.
Also, if you write floating-point binary you're in for a whole nother world of pain.  There are a number of different formats out there, which again might depend on the silicon, the OS, the build-time configuration of the OS or language runtime.  ARM (most phones and tablets) is particularly prone to this.
Some processors also change the size of the integral types under some circumstances.  A modern ARM processor, for example, has an entire second instruction set (Thumb) with smaller data sizes and instructions.  You need to be darn sure the compiler doesn't insert that during optimization, although most language runtimes won't do that during I/O.
Short story: avoid direct binary read/writes of internal data structs if you ever ever will do that on a machine other than one single development machine that will never be upgraded.

#5190123 weird header behavior in gcc and llvm

Posted by Bregma on 30 October 2014 - 06:33 AM

Here's a tip.


Physical dependencies should always be a DAG (directed acyclic graph).  Always.


One aspect of that is that lower-level things should never depend on higher-level things.  A source file down the hierarchy should never depend on one up the same hierarchy.  Same goes with link-time dependencies (libraries, etc).  Directed acyclic graphs.  Only.


Using forward declarations is one of the tools given by the designer of C to allow you to have cyclic logical dependencies without having cyclic physical dependencies.  Logical coupling should be minimized: physical coupling even more so.


Also, header files should only contain the public interface of a module.  The should expose only what needs to be exposed, no more.  They should also be idempotent:  they should include everything they must include, and no more, and be included in any arbitrary order.