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
Namen
Mar 23, 2004
BEES! BEES IN THE CAR! BEES EVERYWHERE!

pseudorandom name posted:

It'd be useful and cheap to NULL out the vtable pointer, though.

Yeah I think I might suggest this. If at the very least it will help me to avoid false positives when I'm digging through dumps.

Adbot
ADBOT LOVES YOU

Jinx
Sep 9, 2001

Violence and Bloodshed
I have a question regarding template templates and specifying a specific class (which has templates) as a parameter. Sometimes it's better to start by showing what the end product should be:

C++ code:

const char name1[] = "Name1";
const char name2[] = "Name1";

C<
    P<int, name1, 6>,
    P<unsigned int, name2, 77u>
> MyClass;

Now I'm struggling to limit the template parameters of class C to only accept types of template class P - does this make sense?

Class P would look like this:
C++ code:
template < typename T, const char * Name, T DefaultValue >
struct P
{
    typedef T value_type;
};
Defining class C thus:
C++ code:
template < typename T, typename.. Args >
struct C
{
};
The first bit of code compiles, however I you can pass any type to class C's template parameters and it will more or less work. I simply cannot figure out the syntax to do this and is it even possible?
I'm not even sure if I will be able to unroll the templates in a way that will produce the end functionality I want at compile time anyway.

pseudorandom name
May 6, 2007

That isn't possible, C++ templates are basically a slightly less terrible version of the C preprocessor.

The thing you are looking for are "concepts", and they died in committee before C++11 was released.

rjmccall
Sep 7, 2007

no worries friend
Fun Shoe

Jinx posted:

The first bit of code compiles, however I you can pass any type to class C's template parameters and it will more or less work. I simply cannot figure out the syntax to do this and is it even possible?
I'm not even sure if I will be able to unroll the templates in a way that will produce the end functionality I want at compile time anyway.

I have no idea what you're trying to accomplish or why it requires that all the variadic arguments be specializations of P instead of just providing a certain set of members, but whatever. You can't restrict the arguments directly, but you can certainly prevent the template from compiling by, somewhere, doing a member access into a class template specialization that is only defined (or only has that member) for specializations of P. That is:

C++ code:

template <class> class only_for_P;
template <class T, const char *name, T value> class only_for_P<P<T,name,value> > {
  typedef int type; // actual type is unimportant
};
...
  void require_P_args(typename only_for_P<Args>::type... args) = delete;

b0lt
Apr 29, 2005

rjmccall posted:

I have no idea what you're trying to accomplish or why it requires that all the variadic arguments be specializations of P instead of just providing a certain set of members, but whatever. You can't restrict the arguments directly, but you can certainly prevent the template from compiling by, somewhere, doing a member access into a class template specialization that is only defined (or only has that member) for specializations of P. That is:

C++ code:
template <class> class only_for_P;
template <class T, const char *name, T value> class only_for_P<P<T,name,value> > {
  typedef int type; // actual type is unimportant
};
...
  void require_P_args(typename only_for_P<Args>::type... args) = delete;

Or you could do a static_assert, which would let you give a better error message:

C++ code:
template<typename T>
struct is_P : std::false_type { };

template<class T, const char *name, T value>
struct is_P<P<T, name, value>> : std::true_type { };

template<typename T>
struct C
{
	static_assert(is_P<T>::value, "welp");
};

rjmccall
Sep 7, 2007

no worries friend
Fun Shoe
Yes, but unfortunately, that's not a legal place for a pack expansion, so you'd have to sink the assert into something that you put in a pack expansion, which is just as much boilerplate.

Marta Velasquez
Mar 9, 2013

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

It seems like the answer to your question should be, "What is the actual problem you are trying to solve?" because it sounds like there's probably a simpler answer.

Paul MaudDib
May 3, 2006

TEAM NVIDIA:
FORUM POLICE

taqueso posted:

edit: posted too slow, not really applicable

Actually it's more applicable than you might think! In some ways it's pretty similar, you're working on a co-processor and it's real easy to have poo poo go bad in unfortunate ways.

I actually like it overall. It's real straightforward and a lot of tasks scale out nicely. Nvidia did a decent job of providing training wheels and making it accessible with the Thrust library as well. If you've ever worked in the STL before it's probably a snap, it's largely similar to that with some specific caveats since it's implemented in parallel.

Paul MaudDib fucked around with this message at 04:43 on May 2, 2014

Paul MaudDib
May 3, 2006

TEAM NVIDIA:
FORUM POLICE

The Laplace Demon posted:

Cast to a pointer of the union type, use memcpy, use placement new (in C++)... What you're asking is unclear, and, by most reasonable interpretations, it's going to be undefined behavior (which in this case will typically trigger a segfault and terminate your program). What are you trying to accomplish with this? Could you show us what you mean with a sample of code?

Ugh, so I have time to play with this and it does seem to be fairly undefined behavior.

What I've been doing in the past:

code:
__global__ void random_2x64_to_4x32(int * rand_output, int N)
{
	threefry2x64_key_t tf_k = {{SEED_DEVICE_LONG[0], SEED_DEVICE_LONG[1]}};
	union{
		threefry2x64_ctr_t c;
		int i[4];
	} u;

	for(int myPos = blockIdx.x * blockDim.x + threadIdx.x;  myPos < N / 4; myPos += gridDim.x * blockDim.x)
	{
		threefry2x64_ctr_t tf_ctr = {{myPos, myPos}};
		u.c = threefry2x64(tf_ctr, tf_k);

		int output_offset = myPos *4;
		rand_output[output_offset+0] = u.i[0];
		rand_output[output_offset+1] = u.i[1];
		rand_output[output_offset+2] = u.i[2];
		rand_output[output_offset+3] = u.i[3];
	}
}
This is a little test kernel based on the Random123 documentation. It generates N random numbers, where N is divisible by 4. It does this by taking two input keys (tf_k) and using them to encrypt two counters (tf_ctr) using the Threefry function (a speed-optimized derivative of Threefish). The output (128 bits of randomness) is stored into a union containing a counter (returned by the function) and sliced into four int32s.

However, tf_ctr, tf_k, and the union are all structs. This means they're too big to fit in registers, and CUDA's automatic dumping ground is "local memory". This is actually global memory (the slowest kind) that is cached in the streaming multiprocessor.

In some cases you can do better by using shared memory instead. There are two ways to assign shared memory: you either declare it as a compile-time constant dimensioned by a #define (static allocation) or you can allocate it dynamically during the kernel launch (which is desirable if you are going to tune the grid topology at run-time). That looks like this:

code:
size_t sharedMemSize = sizeof(threefry2x64_ctr_t) * threads;
random_2x64_to_4x32_sharedMem<<<blocks,threads,sharedMemSize>>>(rand_vector_ptr,N);
code:
__global__ void random_2x64_to_4x32_sharedMem(int * rand_output, int N)
{
	extern __shared__ threefry2x64_ctr_t sharedMem_ctr[];
	extern __shared__ int sharedMem_int[];

	threefry2x64_key_t tf_k = {{SEED_DEVICE_LONG[0], SEED_DEVICE_LONG[1]}};
	 
	int tid = threadIdx.x;

	int myRandUnion_intOffset = tid * 4;

	for(int myPos = blockIdx.x * blockDim.x + threadIdx.x;  myPos < N / 4; myPos += gridDim.x * blockDim.x)
	{
		threefry2x64_ctr_t tf_ctr = {{ myPos, myPos}};

		sharedMem_ctr[tid] = threefry2x64(tf_ctr, tf_k);

		int output_offset = myPos *4;
		rand_output[output_offset+0] = sharedMem_int[myRandUnion_intOffset + 0];
		rand_output[output_offset+1] = sharedMem_int[myRandUnion_intOffset + 1];
		rand_output[output_offset+2] = sharedMem_int[myRandUnion_intOffset + 2];
		rand_output[output_offset+3] = sharedMem_int[myRandUnion_intOffset + 3];
	}
}
Note that both the extern int and extern threefry2x64_ctr_t pointers will point to the same location at run time. So the end result is pretty much equivalent to pointer type casting.

I couldn't make the union-pointing-to-arbitrary-location work, but this accomplishes pretty much the same thing. I feel like this is much less obvious and could run into issues with volatility but my testing shows that the outputs are stable and identical to the base-case above, so I guess it's fine.

There's three different items of interest here, the input counter, the output counter, and the key, all of which are structs wrapping an int64[2]. So I tried it four different ways: everything into local memory, output counter into shared memory, input and output counters into shared memory, and everything into shared memory. Putting the output counter into shared memory produced an 8% speedup, everything else was par or below. Probably due to memory banking issues. I may tweak it a bit to see if I can do better.

I fully realize there's other things that could be improved here too, like a memcpy instead of four individual memory accesses, but it's just a simple little benchmarking experiment and using the numbers individually is a realistic use-case.

Paul MaudDib fucked around with this message at 04:37 on May 2, 2014

Jinx
Sep 9, 2001

Violence and Bloodshed

rjmccall posted:

I have no idea what you're trying to accomplish or why it requires that all the variadic arguments be specializations of P instead of just providing a certain set of members, but whatever. You can't restrict the arguments directly, but you can certainly prevent the template from compiling by, somewhere, doing a member access into a class template specialization that is only defined (or only has that member) for specializations of P.
This is completely valid, but I found the whole "create a fake type and then check for it" to be a bit ugly. I guess I was trying to be clever and find a nicer way.

contrapants posted:

It seems like the answer to your question should be, "What is the actual problem you are trying to solve?" because it sounds like there's probably a simpler answer.
We currently have a lot of boiler plate code when dealing with config files and arguments parsing. Writing something that has a similar interface to Python's argparser class is not particularly difficult, but I really wanted to be able to capture type information for config fields and program arguments within the class definition. Does this make sense?

rjmccall
Sep 7, 2007

no worries friend
Fun Shoe
Okay. So specializations of P describe arguments, with C being an argument parser templated over the set of arguments it supports. The best practice is for C to not care how how those descriptor types were generated; specializations of P have a certain set of public members, and C should just assume that its argument descriptors have all those members. If you mess up, you'll get a crappy diagnostic; just blame the C++ committee for not being able to settle on a concepts proposal.

That Turkey Story
Mar 30, 2003

rjmccall posted:

Okay. So specializations of P describe arguments, with C being an argument parser templated over the set of arguments it supports. The best practice is for C to not care how how those descriptor types were generated; specializations of P have a certain set of public members, and C should just assume that its argument descriptors have all those members. If you mess up, you'll get a crappy diagnostic; just blame the C++ committee for not being able to settle on a concepts proposal.

I just blame Bjarne. The C++0x proposal should have gone in or at least should have been simplified and then gone in in a later standard. Concepts lite is poo poo and doesn't have a clear route to possible improvement.

Marta Velasquez
Mar 9, 2013

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

Jinx posted:

This is completely valid, but I found the whole "create a fake type and then check for it" to be a bit ugly. I guess I was trying to be clever and find a nicer way.

We currently have a lot of boiler plate code when dealing with config files and arguments parsing. Writing something that has a similar interface to Python's argparser class is not particularly difficult, but I really wanted to be able to capture type information for config fields and program arguments within the class definition. Does this make sense?

Would something more like this make sense then?

code:
template < typename T, const char * Name, T DefaultValue1, typename U, const char * Name2, U DefaultValue2 >
class C : public P<T, Name, DefaultValue1>, P<U, Name2, DefaultValue2 >
{
    // Code here
}
Usage:

code:
const char name1[] = "Name1";
const char name2[] = "Name1";

C<
    int, name1, 6,
    unsigned int, name2, 77u
> MyClass;
I'm not sure if this is completely valid. I'm more just trying to understand the problem.

KICK BAMA KICK
Mar 2, 2009

New to C++ (and not very experienced in general). Just saw this paper on C++11 random numbers mentioned in the Interviews thread, and it reminded me that I came across it a while ago and was wondering if the way I implemented it was correct. I guess it does need to check that lo <= hi and I don't know if type checking on the template functions would be appropriate.

Paul MaudDib
May 3, 2006

TEAM NVIDIA:
FORUM POLICE
Broad question here - are there issues inherent to packing multiple data fields into a larger word size that I should be aware of? Like, let's say I want to have an array of bools, or an array of uint4's (4-bit unsigned ints). If I remember, doing an array of bools isn't any more efficient than using a char since C++ aligns to a word. There's the BitSet class, but it doesn't offer more than booleans, and I need something low-level to work on CUDA.

Writing methods to handle this seems to have the following advantages to me. First, easy cross-platform arbitrary length words. I can just specify that I want 4-bit fields (or whatever) and that's that. Greater memory efficiency for small word sizes. And I also think it will have advantages in terms of memory alignment.

Paul MaudDib fucked around with this message at 06:38 on May 7, 2014

Joda
Apr 24, 2010

When I'm off, I just like to really let go and have fun, y'know?

Fun Shoe
Does C++ have garbage collection akin to that of Java? I know you have to explicitly delete items on the heap, but if an item on the stack no longer has any references to it (like if something is referred to exclusively by a pointer, and I set that pointer to nullptr,) does the space automatically get freed up, or is there something I need to do? As far as I've been able to pick up, delete can only be used for items allocated on the heap.

Plorkyeran
Mar 22, 2007

To Escape The Shackles Of The Old Forums, We Must Reject The Tribal Negativity He Endorsed
Items on the stack will get destroyed when they go out of scope even if you do have references to them.

Jewel
May 2, 2009

C++ code:
#include <string>

class SomeObject
{
    std::string m_name;

public:
    SomeObject(const std::string& a_name)
    {
        m_name = a_name;
        printf("Object %s created!\n", m_name.c_str());
    }
    
    ~SomeObject()
    {
        printf("Object %s destroyed!\n", m_name.c_str());
        m_name = "DESTROYED";
    }

    void DoStuff()
    {
        printf("Object %s doing stuff!\n", m_name.c_str());
    }
};

int main()
{
    SomeObject a("a");

    SomeObject* bRef;
    
    {
        SomeObject b("b");
        bRef = &b;
    }

    bRef->DoStuff();
}
I wrote this little thing to show that the pointer would be invalid after b loses scope and gets destroyed. Buuut it isn't. It technically is! But from a quick glance you'd think your code was still working fine.

code:
Object a created!
Object b created!
Object b destroyed!
Object DESTROYED doing stuff!
Object a destroyed!
It's just because the pointer points to the memory and that memory hasn't been written over since there's nothing in between, right? It's technically interpreting invalid data as a SomeObject which just happens to be data left from b, which "works". I talked to Suspicious Dish and they said that if the b initialisation + bRef assignment block was it's own function rather than inline scope, it would have most likely cleaned up the stack of B after the function returned, which would have broken it (unless the function got inline optimized).

For Joda: Any variables that get created from now on can overwrite the space bRef is pointing to, so it's not defined behavior and can/will crash. Don't point to anything you know might go out of scope. If you want to keep it in scope with the pointer and delete it when the pointer goes out of scope, look into std::shared_ptr and std::unique_ptr, they use reference counting.

Jewel fucked around with this message at 01:33 on May 7, 2014

Joda
Apr 24, 2010

When I'm off, I just like to really let go and have fun, y'know?

Fun Shoe
I'm trying to make a linked list for the embedded project I'm working on (because STL isn't readily available,) and I'm trying to rewrite it so it uses the stack rather than the heap. How would you go about instantiating a linked list object for the next pointer without using the heap?

what I have now:
C++ code:
template <class T>
void push_back(T elementToAdd)
{
	LinkedList<T>* temp = this;

	while(temp->next != nullptr)
		temp = temp->next;

	temp->next = new LinkedList(T(elementToAdd));
}
The way I want to do it, the only reference to the new element in the list would be a next pointer, but with the element on the stack in stead of the heap. Based on what you say, I can't figure out how to do that without the element getting destroyed.

E: I should note that C++11 won't necessarily be an option.

Joda fucked around with this message at 01:46 on May 7, 2014

tractor fanatic
Sep 9, 2005

Pillbug
You can't. The whole point of the stack is to have a nice orderly section of memory where in one direction everything's in use and alive, and in another direction you know nothing's in use and it's safe to put your own stuff there. This means the only bookkeeping is changing a single pointer. If you want to keep a region of memory in use in the middle of other memory then you need to do complicated bookkeeping for it, which is what the heap is for.

seiken
Feb 7, 2005

hah ha ha

Joda posted:

The way I want to do it, the only reference to the new element in the list would be a next pointer, but with the element on the stack in stead of the heap. Based on what you say, I can't figure out how to do that without the element getting destroyed.

Correct. That's how the stack works. The stack has storage for the variables used in the current call stack and that's pretty much it. If you want an object to outlive the scope of a single function call, something needs to be allocated on the heap.

Contrast with Java, which doesn't let you use the stack directly for anything other than primitive types (int, float, etc). All other objects live on the heap with only references on the stack.

You can use std::shared_ptr in C++03 for some automatic memory management if your data doesn't have cycles.

Plorkyeran
Mar 22, 2007

To Escape The Shackles Of The Old Forums, We Must Reject The Tribal Negativity He Endorsed
std::tr1::shared_ptr in C++03, but if you don't have std::list you're probably not going to have tr1...

raminasi
Jan 25, 2005

a last drink with no ice
I can't think of a lot of reasons to use C++ if you don't have the STL. Just use C.

Jinx
Sep 9, 2001

Violence and Bloodshed

tractor fanatic posted:

You can't. The whole point of the stack is to have a nice orderly section of memory where in one direction everything's in use and alive, and in another direction you know nothing's in use and it's safe to put your own stuff there. This means the only bookkeeping is changing a single pointer. If you want to keep a region of memory in use in the middle of other memory then you need to do complicated bookkeeping for it, which is what the heap is for.

You "can" by just declaring a char array of some *static* size and using that as a memory buffer (using placement new, for example), however your container will be size limited at compile time, which may not be particularly useful.

It's a bad idea for a lot of reasons - first of which is that, especially on embedded systems, you can cause really bad things to happen if you try to allocate more stack than you have available. Also, because I am a terrible programmer, I don't know off hand whether you're going to have memory alignment problems if you do this sort of thing manually.

GrumpyDoctor posted:

I can't think of a lot of reasons to use C++ if you don't have the STL. Just use C.
I don't agree - classes provide a lot of useful things over and above what just the STL provides (e.g. RAII). I still wouldn't want to implement my own standard library though.

Hiowf
Jun 28, 2013

We don't do .DOC in my cave.

Jinx posted:

You "can" by just declaring a char array of some *static* size and using that as a memory buffer (using placement new, for example), however your container will be size limited at compile time, which may not be particularly useful.

It'll still go out of scope and be overwritten when the function exits. If you meant a static allocation: that won't actually, IIRC, live on the stack (BSS?). You can do it in main and then pass it down to the rest of your program...but why? Stack space is generally limited.

quote:

I can't think of a lot of reasons to use C++ if you don't have the STL. Just use C.

Happens a lot in embedded. You generally just reimplement the parts you need most, and tweak them based on your usage patterns. Take a look at Qt 4 for example: http://qt-project.org/doc/qt-4.8/containers.html

OneEightHundred
Feb 28, 2008

Soon, we will be unstoppable!
Templates are a major improvement over manually building functions with preprocessor macros, RAII is really useful, and operator overloading makes it easy to use more complex math types without writing completely insane code.

Personally, I'd rather use lighter implementations anyway, STL and STL-likes are still really bad about providing legible diagnostics, legible type information (i.e. functions frequently use typedefs from the template class as parameter types even when that type is guaranteed to be one of the template arguments), and reasonable guarantees about the underlying behavior (i.e. I've seen vector::push_back implemented as doubling capacity on expansion and allocating reallocating 1 extra element on expansion). It also includes some extremely questionable design decisions, like the fact that it STILL doesn't support in-place expansion (i.e. realloc) for some reason and been rejecting that feature for ages.

OneEightHundred fucked around with this message at 08:41 on May 7, 2014

qntm
Jun 17, 2009
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?

qntm fucked around with this message at 11:58 on May 8, 2014

Zopotantor
Feb 24, 2013

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

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.

Just assume that references are pointers that don't require using '&', '*' or '->'. That's how the compiler will treat most of them anyway.

References are a syntactic kludge that is needed to allow overloading assignment operators.

Hiowf
Jun 28, 2013

We don't do .DOC in my cave.

qntm posted:

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?

Ownership of objects and expectations on their lifetimes is something that generally must be documented. You'll also see a lot of shared_ptr/unique_ptr/auto_ptr usage to solve ownership problems like this.

Gazpacho
Jun 18, 2004

by Fluffdaddy
Slippery Tilde
A class that doesn't document its pointer retention is badly documented. Aside from that, the way you protect yourself in situations like this is by using scopes to manage the lifetime of both objects together, either by making them instance variables within the same higher-level object, or by declaring them as local variables in the same block.

shrughes
Oct 11, 2008

(call/cc call/cc)

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

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.

qntm posted:

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?

Just avoid using poo poo libraries.

raminasi
Jan 25, 2005

a last drink with no ice
Yeah, any function that accepts a non-const reference (especially a constructor) is doing something weird, and if its weird behavior is not clearly documented somewhere then you should be very wary.

qntm
Jun 17, 2009

shrughes posted:

That code as written is not taking a pointer to that reference parameter.

I meant to write

C++ code:
Ball::Ball(Colour & colour) : iColour(&colour) {
  // etc.
}

Subjunctive
Sep 12, 2006

✨sparkle and shine✨

GrumpyDoctor posted:

Yeah, any function that accepts a non-const reference (especially a constructor) is doing something weird, and if its weird behavior is not clearly documented somewhere then you should be very wary.

Would this be any better if the reference were const?

Marta Velasquez
Mar 9, 2013

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

qntm posted:

I meant to write

C++ code:
Ball::Ball(Colour & colour) : iColour(&colour) {
  // etc.
}

That API is badly broken. It's going to bite you. It should look more like:

C++ code:
Ball::Ball(Colour * colour) : iColour(colour) {
  // etc.
}
It's the kind of thing I would try to avoid, or I'd write a wrapper around it if I have to use it.

Star War Sex Parrot
Oct 2, 2003

Now I'm just left wondering who owns colour and is eventually going to clean it up. Or worse: if it might get deleted before Ball is done with it.

edit: I guess that's what kicked off this whole thing and I should do more than just skim the code in the thread.

qntm posted:

As soon as execution returns from getOneMessage(), 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?

raminasi
Jan 25, 2005

a last drink with no ice

Subjunctive posted:

Would this be any better if the reference were const?

It wouldn't be better library behavior, no. I was just trying to alert qntm to the existence of a red flag here that could possibly be used to avoid getting burned in the future.

qntm
Jun 17, 2009
Okay, I'm glad that this is actually a dangerous oddity rather than something endemic to C++.

This is going to make my life extremely hard though :(

Zopotantor
Feb 24, 2013

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

qntm posted:

This is going to make my life extremely hard though :(

C++ is for men, not boys. :clint:
Insane, masochistic men, but still.

Adbot
ADBOT LOVES YOU

Hiowf
Jun 28, 2013

We don't do .DOC in my cave.

qntm posted:

Okay, I'm glad that this is actually a dangerous oddity rather than something endemic to C++.

:shobon:

quote:

This is going to make my life extremely hard though :(

Allocate colour on the heap, register the pointer(s) in some global list and free when Ball is no longer used. You can wrap Ball to automate it.

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