Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
High Protein
Jul 12, 2009

shrughes posted:

That code as written is not taking a pointer to that reference parameter. It is assigning the field iColour to the value colour. What is the type of iColour? If it's Colour, then it's getting the value copied in. If it's Colour&, then it is a reference to the object (i.e. a pointer, as far as the CPU and object lifetimes are concerned).

This code is just insane poo poo, I can tell. If it were sane, it'd be passing a const Colour & to the constructor and copying it, or passing a Colour *colour so that the caller can see that some funny business might be going on.


Just avoid using poo poo libraries.

Is the insane part the Ball taking the parameter, or the Color taking the parameter?

I thought a constructor accepting a reference, and using this to initialize a member reference it keeps, was actually a good way to infer 'this object is non-optional and must be kept alive for the duration of the new object's lifetime'. For example, I usually use constructor reference parameters if some class needs a [something]manager. Usually these will be const, but that isn't always possible. So basically I don't think there's an issue with the Ball part?

Of course, taking the address of that reference and using it to initialize a pointer is wrong, because that does muddle up the whole thing.

Adbot
ADBOT LOVES YOU

raminasi
Jan 25, 2005

a last drink with no ice

High Protein posted:

I thought a constructor accepting a reference, and using this to initialize a member reference it keeps, was actually a good way to infer 'this object is non-optional and must be kept alive for the duration of the new object's lifetime'. For example, I usually use constructor reference parameters if some class needs a [something]manager. Usually these will be const, but that isn't always possible. So basically I don't think there's an issue with the Ball part?

If you're writing classes that take const reference parameters with undocumented lifetime requirements we should probably take things over to the coding horrors thread.

e: the class definition is not "documentation"

High Protein
Jul 12, 2009

GrumpyDoctor posted:

If you're writing classes that take const reference parameters with undocumented lifetime requirements we should probably take things over to the coding horrors thread.

e: the class definition is not "documentation"

Maybe I/we should be better at explicitly documenting it, but to be honest things like 'const ILogger& logger' or 'ResourceManager& resourceManager' suggest a guarantee of the objects being kept alive, while 'const string& name' does not. Of course deviating from that expectation does require extra documentation. I mean in the color example you obviously expect to not have to keep the color alive.

tractor fanatic
Sep 9, 2005

Pillbug

High Protein posted:

Of course, taking the address of that reference and using it to initialize a pointer is wrong, because that does muddle up the whole thing.

Whether its saving it as a pointer or a reference is irrelevant since it still requires the object to stay alive. But passing the parameter as a pointer and not a reference at least gives a hint that there's some lifetime issues going on here.

High Protein
Jul 12, 2009

tractor fanatic posted:

Whether its saving it as a pointer or a reference is irrelevant since it still requires the object to stay alive. But passing the parameter as a pointer and not a reference at least gives a hint that there's some lifetime issues going on here.

True, but when it's passed as a pointer, someone can pass nullptr (which you won't catch until runtime), or someone can actually try to delete it. I guess though you could say that deleting a pointer that was passed in as a param is just an idiotic thing to do and that, if it's what you want, it should be a unique_ptr that's used as a parameter.
Of course, when it's not a constructor parameter you have no choice but to use a pointer.

rjmccall
Sep 7, 2007

no worries friend
Fun Shoe
It's alright to pass something around as a T&, with an expectation that the caller is responsible for keeping the referent object alive "forever", if that's all basically obvious from T.

For example, if you're writing a server, and T is a class that represents a client session, then (1) you should probably make that class formally non-copyable (or at least not publicly copyable), (2) the session itself should probably be uniquely owned somewhere, and (3) once you've committed to both of those decisions, there's really no harm in passing it around as T& instead of T*, because everybody knows that SomeRandomHelperClass/Function isn't actually going to copy the session.

In contrast, if T is some value type that really does get copied all over the place, then taking it by reference is just asking for problems.

seiken
Feb 7, 2005

hah ha ha
This is a problem I was going back and forth on recently. Suppose you write a library with two classes A and B, where each B references an A and requires that it stick around at least as long as the referencing object:

C++ code:
class A {};
class B {
  // I will do stuff with a
  B(const A* a) : _a(a) {}
  const A* _a;
};
Either you do it as in this example, and explicitly force the users of the library to manage the lifetimes however they want, and document that fact.

Or, you make A a Pimpl-style thin wrapper around something like std::shared_ptr<InternalsOfA>, and then B just copies out the shared_ptr, and then users of the library don't have to worry about anything: B still works if the A goes out of scope or whatever else.

I don't know which I prefer. The first aligns more with the "pay for what you use" philosophy, but the second is just nicer to work with at least if the objects involved are fairly large ones and there aren't going to be billions of them all over the place. If the lifetimes of the objects involved aren't performance-critical, it doesn't really make sense to force users of every library to implement their own object dependency system just because they might occasionally have a dependency structure that can be implemented in a super-efficient way.

seiken fucked around with this message at 00:19 on May 8, 2014

Marta Velasquez
Mar 9, 2013

Good thing I was feeling suicidal this morning...
Fallen Rib

seiken posted:

This is a problem I was going back and forth on recently. Suppose you write a library with two classes A and B, where each B references an A and requires that it stick around at least as long as the referencing object:

C++ code:
class A {};
class B {
  // I will do stuff with a
  B(const A* a) : _a(a) {}
  const A* _a;
};
Either you do it as in this example, and explicitly force the users of the library to manage the lifetimes however they want, and document that fact.

Or, you make A a Pimpl-style thin wrapper around something like std::shared_ptr<InternalsOfA>, and then B just copies out the shared_ptr, and then users of the library don't have to worry about anything: B still works if the A goes out of scope or whatever else.

I don't know which I prefer. The first aligns more with the "pay for what you use" philosophy, but the second is just nicer to work with at least if the objects involved are fairly large ones and there aren't going to be billions of them all over the place. If the lifetimes of the objects involved aren't performance-critical, it doesn't really make sense to force users of every library to implement their own object dependency system just because they might occasionally have a dependency structure that can be implemented in a super-efficient way.

Because it's a library, would it make sense to have a third class C that owns both A and B objects, and the library user accesses the A and B objects through object C? Basically C becomes a thin wrapper that ensures object A and object B are created and destroyed together.

Of course, this may not make sense in the context of your library, though.

seiken
Feb 7, 2005

hah ha ha

contrapants posted:

Because it's a library, would it make sense to have a third class C that owns both A and B objects, and the library user accesses the A and B objects through object C? Basically C becomes a thin wrapper that ensures object A and object B are created and destroyed together.

I'm not entirely sure what advantage this has over the second solution I discussed. You can ensure A lives long enough without some other class C, and the point is to allow many-to-one relationships, so I don't know what purpose C serves here.

Look Around You
Jan 19, 2009

Hey, this is sort of a cross post from the gamedev thread, but I that this part of it would go better in here:

Do any of you guys have a good recommendation for a hashtable library for C? I know of GLib, but I don't particularly want to use it because it's a pretty heavy dependency for what I'll be using of it (mostly just data-structures). Failing that, do you guys have any recommendations for implementing my own (especially re: hash functions and size constants)?

Deus Rex
Mar 5, 2005

Look Around You posted:

Do any of you guys have a good recommendation for a hashtable library for C? I know of GLib, but I don't particularly want to use it because it's a pretty heavy dependency for what I'll be using of it (mostly just data-structures). Failing that, do you guys have any recommendations for implementing my own (especially re: hash functions and size constants)?

Have you considered using C++ instead?

Look Around You
Jan 19, 2009

Deus Rex posted:

Have you considered using C++ instead?

Yeah, I was planning on it actually, but I don't want to write an RAII wrapper around SDL functions and I can't use C++11 features with SFML because my mac is too old to run OSX v10.8 or 10.9. I'm not comfortable enough with C++ that messing around with it seems appealing unless I'm using the C++11 features that alleviate a lot of the concerns I have when I'm writing it.

e: by concerns I mean having to constantly double check a library reference/language FAQ to make sure I'm using the write type/methods or constantly try to compile to make sure my code is being parsed to do what I intended it to do semantically. I'm way more comfortable working in C, I just can't find a good data structure library.

Look Around You fucked around with this message at 08:03 on May 8, 2014

Sinestro
Oct 31, 2010

The perfect day needs the perfect set of wheels.

Look Around You posted:

Hey, this is sort of a cross post from the gamedev thread, but I that this part of it would go better in here:

Do any of you guys have a good recommendation for a hashtable library for C? I know of GLib, but I don't particularly want to use it because it's a pretty heavy dependency for what I'll be using of it (mostly just data-structures). Failing that, do you guys have any recommendations for implementing my own (especially re: hash functions and size constants)?

I've never used it in a real project, but I've played with uthash a little, and other than ugliness that comes from C's lack of generics, it was pretty nice.

Paniolo
Oct 9, 2007

Heads will roll.
I'm having trouble understanding why this doesn't work:

code:
template <typename TRange>
class is_range
{
public:

    template <typename T>
    static auto test(T&) -> decltype(begin(T));

    static void test(...);

    static const bool value =
        !std::is_same<decltype(test(std::declval<TRange>())), void>::value;
};
It's always false, and if I call the test function directly:

code:
std::vector<int> vec;

is_range<decltype(vec)>::test(vec);
The linker error I get indicates it's resolving to the ... overload instead of the template function. However, the following compiles just fine:

code:
std::vector<int> vec;

decltype(begin(vec)) iter = begin(vec);
So I'm missing something.

Slurps Mad Rips
Jan 25, 2009

Bwaltow!

Paniolo posted:

I'm having trouble understanding why this doesn't work:

code:
template <typename TRange>
class is_range
{
public:

    template <typename T>
    static auto test(T&) -> decltype(begin(T));

    static void test(...);

    static const bool value =
        !std::is_same<decltype(test(std::declval<TRange>())), void>::value;
};
It's always false, and if I call the test function directly:

code:
std::vector<int> vec;

is_range<decltype(vec)>::test(vec);
The linker error I get indicates it's resolving to the ... overload instead of the template function. However, the following compiles just fine:

code:
std::vector<int> vec;

decltype(begin(vec)) iter = begin(vec);
So I'm missing something.

You forgot to make the true check look like decltype(begin(declval<T&>())) for its return type. You're basically passing a type directly to begin otherwise causing it to fail.

Marta Velasquez
Mar 9, 2013

Good thing I was feeling suicidal this morning...
Fallen Rib

seiken posted:

I'm not entirely sure what advantage this has over the second solution I discussed. You can ensure A lives long enough without some other class C, and the point is to allow many-to-one relationships, so I don't know what purpose C serves here.

My thought was that, if you eventually need multiple objects of class A or B, you can easily add them to class C without having to change the user API. It happens to me a lot due to feature creep.

I'm mostly just curious about a non-STL solution. I can't use C++11 at work, and I can't use auto_ptr "in case we move to C++11" because it's deprecated in that standard.

Deus Rex
Mar 5, 2005

qntm posted:

I'm new to C++. I am using a class whose constructor has a reference parameter. I just discovered that inside the constructor, the class actually takes a pointer to this reference parameter and stores that pointer as a member variable.

C++ code:
Ball::Ball(Colour & colour) : iColour(colour) {
  // etc.
}
This means that my code, which currently looks like this:

C++ code:
Ball getOneBall(void) {
  Colour colour("red");
  return Ball(colour);
}
is broken. As soon as execution returns from getOneBall(), colour is taken off the stack, but the returned Ball still retains a pointer to it. Later, when the Ball tries to use the pointer, everything explodes. Obviously, my mistake was to assume that the constructor would take a copy of colour for its own use. Instead I need to make sure that colour remains on the stack (or heap) for at least as long as the returned Ball does.

My question is this: how can I protect myself from this kind of mistake in the future? I didn't realise until later that colour was being passed by reference, which should have been a clue, which I'll put down to inexperience. But beyond this, I'm finding it really hard to reason about C++ code in general. Surely, any method which accepts reference parameters could do something similar. Surely any methods called by that method could do the same. Does this mean I need to laboriously inspect the implementation of every method I ever call, directly or indirectly, just to catch this kind of behaviour? What if the implementation isn't available?

It would be neat to see regions added to C++ in the future because named lifetimes make this a compile-time error in Rust.

Rust code:
struct Color(u8, u8, u8);

// 'ob is a named lifetime parameter corresponding to the
// lifetime of a constructed Ball
struct Ball<'ob> {
    // color is a reference to a Color whose lifetime is the 
    // same as or "longer" than the Ball
    color: &'ob Color
}

// red has the special lifetime static
static red: Color = Color(255, 0, 0);

fn newBall
fn getOneBall() -> Ball {
    // compiles because red lives long enough
    return Ball { color: &red }; 
}

fn getAnotherBall() -> Ball {
    let green = Color(0, 255, 0);
    // doesn't compile because green doesn't live long enough
    return Ball { color: &green };
}

Suspicious Dish
Sep 24, 2011

2020 is the year of linux on the desktop, bro
Fun Shoe

Look Around You posted:

Hey, this is sort of a cross post from the gamedev thread, but I that this part of it would go better in here:

Do any of you guys have a good recommendation for a hashtable library for C? I know of GLib, but I don't particularly want to use it because it's a pretty heavy dependency for what I'll be using of it (mostly just data-structures). Failing that, do you guys have any recommendations for implementing my own (especially re: hash functions and size constants)?

Just Use GLib

Paul MaudDib
May 3, 2006

TEAM NVIDIA:
FORUM POLICE
The compiler is smart enough to prune useless function parameters if I make it obvious that they're not being used, right?

I have roughly the following:

code:
#define VALIDATION 1

int * validation_data_array;

void setup(){
...
	if(VALIDATION)
		validation_data_array = ...;
	else
		validation_data_array = NULL;
...
}
I then have a function where I pass the pointer in as a parameter, and it in turn passes an adjusted pointer to a child, which writes output data if the define evaluates to true.

code:
__global__ void master_func(int * validation_data_array)
{
	int output_offset = ...;
	child_func( ..., validation_data_array + output_offset, ...);
}

__device__ void child_func(..., int * output_validation, ...)
{
	...do stuff...

	if(VALIDATION)
		*output_validation = myDataToValidate;
}
If the only times the parameter is used in a function is inside of an if statement that is #define'd to never evaluate true, or as a parameter to functions where it's never used, the compiler should be smart enough to prune that, right? I don't want a bunch of useless parameters cluttering up my registers in the release build. If necessary I can throw in a mess of #ifdef guards but that'll look messy.

Paul MaudDib fucked around with this message at 03:54 on May 9, 2014

Plorkyeran
Mar 22, 2007

To Escape The Shackles Of The Old Forums, We Must Reject The Tribal Negativity He Endorsed
That's impossible if the function isn't inlined, and not really applicable if it is.

Subjunctive
Sep 12, 2006

✨sparkle and shine✨

Second-guessing the compiler's register allocation is a trail of tears, IMO. See what the profiler says, I doubt it'll consider the extra caller-saves unused register pressure to be a big issue.

Suspicious Dish
Sep 24, 2011

2020 is the year of linux on the desktop, bro
Fun Shoe

Plorkyeran posted:

That's impossible if the function isn't inlined, and not really applicable if it is.

Or if it's scoped properly, like a static function.

rjmccall
Sep 7, 2007

no worries friend
Fun Shoe
If the callee doesn't use a parameter, it can immediately drop that value, so that it does not contribute to internal register pressure there. That is a ubiquitous optimization. It will still occupy parameter slots at the point of the call, however, and the callee may still do work unnecessary work computing the argument.

If a specific caller knows the exact implementation which will be called, and it can prove that the parameter is unused in that implementation, it can leave the parameter uninitialized and apply DCE to the argument computation. But note that this requires knowing the exact implementation, and being merely semantically equivalent on a high level is not good enough; consider a C++ inline function which is compiled under different optimization settings in different translation units. And again, the parameter will still occupy slots at the point of call, even if those slots are uninitialized/ignored on both sides.

Actually eliminating the parameter, so that it does not even temporarily occupy parameter slots, requires being able to prove something about every possible use of the function. More or less, this means that the function must have internal linkage and never have its address taken.

Paniolo
Oct 9, 2007

Heads will roll.

SAHChandler posted:

You forgot to make the true check look like decltype(begin(declval<T&>())) for its return type. You're basically passing a type directly to begin otherwise causing it to fail.

Hurray for obvious mistakes. Works great now, thanks.

Zopotantor
Feb 24, 2013

...und ist er drin dann lassen wir ihn niemals wieder raus...

Paul MaudDib posted:

code:
	child_func( ..., validation_data_array + output_offset, ...);

That's undefined behavior if 'validation_data_array' is NULL.

Megaman
May 8, 2004
I didn't read the thread BUT...
A few questions from a total C++ noob:

I'm starting to get comfortable with Python, I use it every day for my sysadmin job, but I've decided to jump into C++ and attempt mediocre game development. I'm going by tutorials on Youtube and snippets here and there from random sites, but it's a massive learning curve. Where can one start? It it smart to jump head first into SDL+OpenGL development? This is where I'm starting. Also, how long does it take to becoming mildly proficient at something like this? And thirdly, is it possible to include compile flags inside my main file, or do I absolutely have no choice but to make a make file?

Ericadia
Oct 31, 2007

Not A Unicorn

Megaman posted:

A few questions from a total C++ noob:

I'm starting to get comfortable with Python, I use it every day for my sysadmin job, but I've decided to jump into C++ and attempt mediocre game development. I'm going by tutorials on Youtube and snippets here and there from random sites, but it's a massive learning curve. Where can one start? It it smart to jump head first into SDL+OpenGL development? This is where I'm starting. Also, how long does it take to becoming mildly proficient at something like this? And thirdly, is it possible to include compile flags inside my main file, or do I absolutely have no choice but to make a make file?

I can't speak confidently to most of those questions, but re: where to start? If you are determined to do game development right away, the Lazyfoo.net tutorials are good, although the kind of code he shows you wouldn't be useful for large projects. This short tutorial series shows you how to set up a state machine, load in resources, do sprite animations, and get a very basic game running, however, you should do some of the lazyfoo tutorials first as the Staniks tutorials expect you to already know SDL basics like creating a window, renderer, etc.

If you are baffled by C++ basics, take a look at Accelerated C++, it's a pretty old book but still useful.

I just started making games back in february and I don't feel like I'm great at it yet, but I at least feel confident enough to understand most of what people are talking about over in the Game Development Thread.

Also, have you considered using Pygame? It's a python wrapper for SDL.

Tetraptous
Nov 11, 2004

Dynamic instability during transition.
C++ is a very flexible language which allows you to program in a number of different styles--it takes a very long time to become 'proficient'. I wouldn't expect to be making games in C++ any time soon! How long it takes depend a lot on how often you use the language.

Compilation flags can't (generally) be incorporated into the source. Many IDEs will allow you to store them in project files, and of course Makefiles work too. For simple programs, even entering them on the command line is fine. If you're using GCC I strongly recommend compiling with -Wall. GCC defaults are rather permissive! I think most of the tutorials you'll find online are rather worthless. I'd just keep referring to the documentation for the STL and any other libraries you'd like to use and keep reading from places like this thread, to blogs, even to StackOverflow and try to get a handle on what the 'best practices' are as you try to complete your project.

netcat
Apr 29, 2008
Just a possibly dumb question. Why does the following work (http://ideone.com/HKWQGn):

C++ code:
#include <iostream>
#include <memory>
using namespace std;

struct Foo
{
	Foo* my_ref;
	int z;
	
	~Foo()
	{
		std::cout << "z=" << z << " -> my_ref->z=" << my_ref->z << std::endl;
	}
};

int main() {

	{
		std::unique_ptr<Foo> a{new Foo};
		std::unique_ptr<Foo> b{new Foo};
		
		a->z = 1;
		b->z = 2;
		
		b->my_ref = a.get();
		a->my_ref = b.get();
	}

	return 0;
}
prints:
z=2 -> my_ref->z=1
z=1 -> my_ref->z=2

b gets destroyed before a. How come I still get valid output when a gets destroyed if the pointer to b got destroyed earlier?

Vanadium
Jan 8, 2005

Use-after-free is undefined behavior, not defined-to-be-garbled behavior. It doesn't work, it just doesn't fail in a way that's distinguishable from it working.

Valgrind notices that it fails if that helps.

OzyMandrill
Aug 12, 2013

Look upon my words
and despair

If you did it in debug in Visual C, you should get:

z=2 -> my_ref->z=1
z=1 -> my_ref->z=-16843010

as the debug runtime fills freed memory with '0xfe', and '0xfefefefe' is -16843010.

In release, it will still be 2, as the C runtime will generally not change freed memory as it is a pointless waste of cycles - it is up to you to make sure memory is initialised after allocation.

xgalaxy
Jan 27, 2004
i write code
You should be using make_unique too btw, instead of newing yourself.
Edit: I keep forgetting that is not in C++ 11 but 14...

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe
So I have a C++ book that I've had for a while, and an early chapter covers integer datatypes including signedness vs. unsignedness. It covers what happens when unsigned integers overflow, but then it goes on to say that the equivalent thing happens for signed integers. (i.e. it gives the impression that INT_MAX + 1 will be INT_MIN.) And I know from various other sources that this needn't be the case and that assuming that it is can lead to obscure bugs due to e.g. compiler optimisations that take the opportunity to assume that signed integer overflow won't happen.

So I was thinking how would you write a function that you could use when you wanted to add signed integers and reliably get the "wrap around" behaviour that unsigned integer overflow is guaranteed to give. That is, how would you write a function that made the statements about signed integer addition in the book true. Let me check whether this is a solution. Some of it is based on skimming through Stack Overflow pages and other resources.

code:
int signed_int_addition_with_wraparound (int const & a, int const & b) {
    if (a > 0 && b > 0 && INT_MAX - a < b) {
        return (INT_MIN + a) + (INT_MIN + b);
            // This avoids overflow because
            //     a + b > INT_MAX
            // and therefore
            //     a + b + 2*INT_MIN > INT_MAX + 2*INT_MIN
            //                       = INT_MAX + 2*(-INT_MAX - 1)
            //                       = -INT_MAX - 2
            //                       = INT_MIN - 1
            // and so (a + b + 2*INT_MIN) is representable.
            // Note that the brackets are important (at least for clarity).
    }
    if (a < 0 && b < 0 && INT_MIN - a > b) {
        return (INT_MAX + a) + (INT_MAX + b);
            // This avoids overflow because
            //     a + b < INT_MIN
            // and therefore
            //     a + b + 2*INT_MAX < INT_MIN + 2*INT_MAX
            //                       = INT_MIN + 2*(-INT_MIN - 1)
            //                       = -INT_MIN - 2
            //                       = INT_MAX - 1
            // and so (a + b + 2*INT_MIN) is representable.
            // Again, the brackets are important (at least for clarity).
    }
    return a + b;
}
I guess this could be made into a template

code:
template <typename T, T min_value, T max_value>
T signed_integer_addition_with_wraparound (T const & a, T const & b) {...}
or even a template with only one template parameter (T) by replacing min_value and max_value throughout with expressions involving sizeof().

Is there a way to give a name to a specific specialisation of a templated function, such as making signed_int_addition_with_wraparound mean the same thing as signed_integer_addition_with_wraparound<int> (so that you don't have to type the <int> bit everywhere, or even care that it's templated)? (Searching for information on templates is complicated - there is a lot of terminology to keep in mind.)

I haven't tested the code I wrote in this post btw, which may be obvious if there are silly syntactical errors or whatever.

Hammerite fucked around with this message at 16:23 on May 13, 2014

Marta Velasquez
Mar 9, 2013

Good thing I was feeling suicidal this morning...
Fallen Rib

Hammerite posted:

Is there a way to give a name to a specific specialisation of a templated function, such as making signed_int_addition_with_wraparound mean the same thing as signed_integer_addition_with_wraparound<int> (so that you don't have to type the <int> bit everywhere, or even care that it's templated)? (Searching for information on templates is complicated - there is a lot of terminology to keep in mind.)

code:
typedef signed_integer_addition_with_wraparound<int> signed_int_addition_with_wraparound;

Plorkyeran
Mar 22, 2007

To Escape The Shackles Of The Old Forums, We Must Reject The Tribal Negativity He Endorsed

Hammerite posted:

Is there a way to give a name to a specific specialisation of a templated function, such as making signed_int_addition_with_wraparound mean the same thing as signed_integer_addition_with_wraparound<int> (so that you don't have to type the <int> bit everywhere, or even care that it's templated)? (Searching for information on templates is complicated - there is a lot of terminology to keep in mind.)

You don't have to include the <int> if both arguments actually are ints. Template type arguments are deduced by the compiler for functions and only have to be specified if it's ambiguous.

Paul MaudDib
May 3, 2006

TEAM NVIDIA:
FORUM POLICE

Megaman posted:

Where can one start? It it smart to jump head first into SDL+OpenGL development? This is where I'm starting. Also, how long does it take to becoming mildly proficient at something like this?

First off, it'll take a bit to get a handle on C++ development. Compared to something like Python I think C++ tends to be a rather fragmented working environment. There's a ton of legacy baggage in terms of standards, coding styles, and libraries. The C++ specification dates back to '82 and I'd generally describe it as some syntactic sugar on top of C (some stuff isn't, eg generics, but a lot is), which dates all the way back to '72. The actual original standards are pretty basic (targeted at OS-level stuff), so basically everyone implements at least some of the later extensions to some degree, but the availability and strictness of this often varies by compiler and small stuff will frequently break if you use a different compiler. You can definitely use it like Java or Python or another modern high-level language (especially the C++ STL), but you don't have to look at it too hard before the veneer starts peeling off and you're dealing with pointer arithmetic and memory allocation/leaks and other stuff where it's important to have at least a cursory understanding of what's going on at a low level. It's helpful to turn off optimizations when writing/debugging a program, this prevents the compiler from optimizing away variables you want to examine and I think it tends to make errors throw earlier and more helpfully. Debug mode may also help but it has a huge performance impact and sometimes doesn't play nicely with other libraries/extensions.

OpenGL is basically the same thing times a billion. The GL standard dates back to the SGI IRIS and OpenGL has been around since 1992, so there's a bunch of slightly different versions out there. In the earlier standards, there was something called the fixed function pipeline that gave you a basic framework to get started, however this was removed in 2009 with the OpenGL 3.1 standard, so a lot of tutorials out on the web are essentially broken (anything involving functions like "glRotate" or "glTranslate" or "glPushMatrix" or "glLoadIdentity"). It's not terribly hard to replicate it if you understand what it did, and you'll be much better off learning the new way, but it's a bit of a barrier to getting started. There's also about a dozen different "helper" libraries to handle the boring plumbing work, everyone uses a slightly different one, all of them are a little different, and half of them haven't been updated in years. I haven't used SDL but it looks like that's what SDL does, so I'd go with that.

Apart from the mechanical stuff, it's pretty important to have a grasp on matrix math because computer graphics are basically just a shitload of matrices. Get ready to learn everything you never wanted to know about coordinate space manipulation, you're going to learn all about manipulating them and translating them and nesting them inside other coordinate spaces. It's also probably a good idea to have a basic understanding of the process of rasterizing, which is how you go from a stack of vertex coordinates and transformation/projection matrices to a nice rendered bitmap (the FF pipeline used to do a good chunk of this). This is particularly fragment/vertex shaders (GL Shader Language), which do a lot of the actual work in rasterizing. You should also understand the different components of lighting (ambient/diffuse/specular). Once you have all the rest of that, the secret sauce is mostly just algorithms that let the shaders generate realistic lighting effects in reasonable amounts of time.

I'd personally look at Lighthouse3d or someone like that, as well as Nate Robin's demos, which I found to be very straightforward and helpful. It's hard to say how long it will take you to become "mildly proficient" particularly since you're also learning C++ at the same time. Assuming picking up C++ as you go isn't crippling you too bad, maybe 4 hours to get to an OpenGL "hello world", then probably at least 8 solid hours to have a basic working understanding of all the coordinate stuff and the technical stuff? If everything goes right you can display a 2d/3d teapot in no time flat but it'll take a bit to do useful things. When things don't work right, it's pretty easy to get bogged down. Given how much of this is math, shotgun debugging is doomed to failure (even with educated guesses) and you should go look at something working (hello Nate Robin) and see where yours is different.

Even after you have the basics there's still tons of fun issues like gimbal lock that'll eventually pop up. Overall it's just a big, big topic and there's lots to learn. Since Python is so great at matrix stuff (see: Numpy) you might even be better off learning OpenGL there and then trying to port what you do over to C++, so at least you know you're fighting C++ syntax instead of bugs in your OpenGL code. I'd start on the simple end of things as much as possible - maybe a 2d game or something like an animated screensaver, and then work your way from there. The deep end is basically as deep as you want to go, game design is something that can take teams of people years to do.

If clicking a few of those links made your eyes glaze over, what you want is not OpenGL+SDL but rather a game engine, which will have you working in objects and scripts rather than vertices and shaders. So far I'm a big fan of Unity Engine, which lets you write in C# and Javascript and offers a basic free version to play with. The development environment is great - it's all right there. If you're really wedded to C++, Unreal Engine 4 can be had for the tire-kicking-good price of $20 per month plus 5% of gross revenue if you sell your game (if you just want to play around, pay for a month, download, and cancel). It's definitely more complex and less mature than Unity though.

Paul MaudDib fucked around with this message at 01:21 on May 13, 2014

Lemon King
Oct 4, 2009

im nt posting wif a mark on my head

I've been working with C/C++ for a few days now and I'm kinda curious about what compiler I should be using.
I started off with MinGW that came stock with Code::Blocks until I found out there were issues and now I'm using MinGW from Sourceforge.

Subjunctive
Sep 12, 2006

✨sparkle and shine✨

Lemon King posted:

I've been working with C/C++ for a few days now and I'm kinda curious about what compiler I should be using.
I started off with MinGW that came stock with Code::Blocks until I found out there were issues and now I'm using MinGW from Sourceforge.

All my experiences with MinGW have ended in tears, and then when I had to use a debugger the tears were of blood. Use MSVC express, drive it from nmake/CMake/gmake/gyp as you choose.

MGN001
May 12, 2012

If I'm writing a function that has an array as a parameter, is there any difference between

code:
type function_name(type * name)
and

code:
type function_name(type name[])

Adbot
ADBOT LOVES YOU

Gazpacho
Jun 18, 2004

by Fluffdaddy
Slippery Tilde
In the specific context of a function argument declaration, the second is always compiled as if it were the first. If you're looking for a guideline, the standard always uses the first form in library function declarations.

This only applies to the first subscript of the array, so a char[2][2] parameter becomes a pointer to char[2], and not a char **.

Gazpacho fucked around with this message at 07:41 on May 13, 2014

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply