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
The1ManMoshPit
Apr 17, 2005

quote:

references...dynamically sized piece of memory

While it isn't possible to create dynamically sized memory in a reference, people often forget that you can still use solely references to refer to objects on the heap. For instance you can do this:

code:
SomeObject& ref = *(new SomeObject());

    . . . . . . . .

delete &ref;
Which is admittedly pretty pointless except that you can avoid dereferencing syntax if it bothers you. However, it means you can do something slightly more useful like this:

code:
class RefExample
{
   SomeOtherObject& mRef;
public:
   RefExample(const tParams& params) 
    : mRef(SomeOtherObjectFactory::GetInstance()->CreateSomeOtherObject(params))
   {}
   ~RefExample() { mRef.Destroy(); }

  //other methods using mRef go here
};
Where the factory method returns a reference instead of the usual pointer which is neat, anyways, if not really all that different from just using a pointer. One caveat here is that you're basically up poo poo creek if memory allocation fails for whatever reason. While you could maybe recover in the second case by creating a default "SomeOtherObject" that indicates error (which is ugly), in the first case there is really no way to cleanly handle the out of memory error except through some ridiculous overloading of the new operator.

So yeah that's pretty pointless all around but it's something to keep in mind if you ever want to make your C++ code look more like java :haw:

Adbot
ADBOT LOVES YOU

The1ManMoshPit
Apr 17, 2005

On line 82 you have "temp = findMin" when it looks like you mean to have "temp->key = findMin->key"

When you remove '3' it has both children, and its right child has no children, so it executes that code which has no effect. I don't know why your program crashes after that.

Also line 9 should set parent to be NULL instead of allocating a new node that you never delete. Also you don't handle the case where you delete the root node and it has less than two children and you only seem to have solved the case where it has two children by accident.

Also have you never heard of recursion before or what?

The1ManMoshPit fucked around with this message at 05:29 on Sep 26, 2009

The1ManMoshPit
Apr 17, 2005

Nomnom Cookie posted:

In all seriousness, can you point me to a decent recursive remove()? Doing recursive insertion, lookup, and traversal on a vanilla BST is quite simple, but when this assignment came up in college I ended up writing an iterative remove().

Just whipped this up now over a coffee. Not sure if it's bullet-proof but it passes one test case so it's good enough for me.

http://pastebin.com/f52990ca5

The1ManMoshPit
Apr 17, 2005

floWenoL posted:

Suggesting recursion over iteration for tail-recursive algorithms is dumb.

Recursive calls when dealing with trees are easier to write, easier to read and easier to understand, as well as being smaller amounts of code making them easier to debug. If you're actually in a position where performance of recursive vs. iterative calls matters hopefully you aren't posting on the internet asking for help implementing basic functionality.

The1ManMoshPit
Apr 17, 2005

Recursive functions for recursive data structures seems natural to me but then again I am also a stupid baby.

The1ManMoshPit
Apr 17, 2005

GrumpyDoctor posted:

You do understand that he's talking specifically about tail recursion, right?

Changing fib(n) types of tail-recursive calls into a loop and changing things which actually make different calls along different branches of execution and do more than return a value are not the same thing. Manually performing tail-call optimization on non-trivial recursive code is not something most people should be thinking of doing without a real reason, like having to deal with large data sets or working on an architecture with a small stack size.

If you can show me how to write 'remove' without any of the following things (which are typical of iterative tree code) then I will believe that you can write iterative code that is as dead simple as the recursive equivalent:
  • Breaking the logic up into two discreet steps, "find" and "action"
  • Checking the termination condition in more than one place
  • Using a while(true) ... break
  • Using a goto

And if you don't understand why those things are more error-prone than just using recursion then I don't know what to tell you.

The1ManMoshPit
Apr 17, 2005

RussianManiac posted:

If you are too stupid to not know how to re-implement recursive function iteratively, you should probably not be writing recursive functions in first place, or any code for that matter.

Ah yes the classic "it's not more error prone when I do it :smug:" defense.

Avenging Dentist posted:

Recursive solutions do this too, you know.

Not in the way I was talking about you jerk!

The1ManMoshPit
Apr 17, 2005

RussianManiac posted:

What are you talking about? Recursive solutions are error prone too. They may take less lines but it doesn't mean you didn't make some stupid mistake.

This is a stupid argument. You should be using recursive functions if you feel like it, unless you want to optimize something and compiler doesn't unroll it for you.

Yeah you're right this is a dumb argument and I am dumb for pressing the issue.

quote:

Did you discover recursive functions last week or something?

I googled it after the guy asked his question and was astonished at the new world that lay before me.

The1ManMoshPit
Apr 17, 2005

UberJumper posted:

Can this lead to problems? Since wont this essentially be unsafe since the compiler thinks that nothing will change in this class during optimization? I am looking at like 45 classes right now and its littered all over with them.

This is a pretty common misconception, but compilers can't optimize around constness in c++ because, as you've demonstrated, they can't really know that you won't modify the object in a const method call. You can obtain a pointer to a member or have one passed into the method, and then there's the existence of const_cast and the mutable keyword...

The1ManMoshPit
Apr 17, 2005

Do these things need to be allocated separately on the heap? Why not just do

code:
    std::vector<foo> v(1000000000000, foo());
?

EDIT: oops I missed the "single big allocation is not good" from your original post. Do you really need a zillion different versions of this object, or can you create a reusable pool or something ala the flyweight pattern?

The1ManMoshPit fucked around with this message at 17:51 on Dec 10, 2009

The1ManMoshPit
Apr 17, 2005

I usually write my member accessors following this pattern

code:
class A
{
public:
    error_state* getFoo(const int** outFoo) const;
private:
    int* foo;
};

error_state* A::getFoo(const int** outFoo) const throw(GlobalStateArrayNotAllocatedException, GlobalStateArrayReturnedNullException)
{
    if (GlobalStateArray::instance() == NULL)
        throw GlobalStateArrayIsNullException();

    error_state* return_state = NULL;

    if (this == NULL)
    {
        return_state = GlobalStateArray::instance()->getError(kThisIsNull);
    }
    else if (outFoo == NULL)
    {
        return_state = GlobalStateArray::instance()->getError(kArgumentIsNull);
    }
    else
    {
        *outFoo = foo;
        return_state = GlobalStateArray::instance()->getError(kSuccess);
    }

    if (return_state == NULL)
    {
        throw GlobalStateArrayReturnedNullException();
    }

    return return_state;
}

The benefit of this is that I cover all cases. Now the calling code knows exactly what went wrong internally!

code:
A a;

int* aFoo = NULL;

try
{
    error_state* es = a.getFoo(&aFoo);

    if (GlobalStateArray::instance() == NULL)
        throw GlobalStateArrayIsNullException();

    switch (GlobalStateArray::instance()->decodeError(es))
    {
        case kThisIsNull:
        case kArgumentIsNull:
            throw CompilerErrorTurnedLocalStackVariableNullException();
            break;
        case kSuccess:
            if (aFoo == NULL)
                throw FooWasNULLException();
            break;
        default:
            throw UnknownGlobalErrorStateReturnedException();
    }
}
catch (const GlobalStateException& e)
{
    logError("Global state exception logged at %s:%d", __FILE__, __LINE__);
    throw LoggedExceptionWrapper<GlobalStateException>(e);
}

// now we can do something with aFoo!
Now I'm guarded against every possible error that a programmer could have made, totally safe to call this, as long as you just remember to follow the simple calling procedure (unlike some asstard junior programmers I work with ;)).

The1ManMoshPit
Apr 17, 2005

Is Update() declared virtual in the base Object class?

The1ManMoshPit
Apr 17, 2005

Staggy posted:

Yep.

code:
virtual void Update(float dt){};

Without more code it's going to be pretty hard to know exactly what your problem is. Are you sure the objects you're inserting into your list are actually instances of the derived classes and not the base class?

The1ManMoshPit
Apr 17, 2005

edit: Wait a minute my terrible goonsay joke wasn't even right dammit. :smith:

The1ManMoshPit fucked around with this message at 05:13 on Feb 16, 2010

The1ManMoshPit
Apr 17, 2005

Sweevo posted:

When using typdef'd structs:
code:
typedef struct
{
    ...
} TYPE;
I notice from browsing random code that some people give the structure a tag:
code:
typedef struct type_t
{
    ...
} TYPE;
...even if the tag is never used anywhere. Is it just for style or portability reasons, or are there good reasons for not using un-tagged structs?

You can't predeclare a typedef. So you can't have (in a header)

code:
typedef struct TYPE;  /* doesn't work obviously */
void doSomething(TYPE* t);
But you can have

code:
struct type_t;
void doSomething(struct type_t* t);
For this reason, if you want to reference a struct in a header without including its header in your header, you need to tag the struct. So it's standard practice in case the struct needs to be predeclared for any reason.

The1ManMoshPit
Apr 17, 2005

What you have posted is no different from having globally accessible functions in a namespace and global variable declarations in a 'private' header that is only included by your game logic, except it is more confusing and ugly. There's no reason to structure your code that way at all.

Why does there need to be a Window class if I am never going to instantiate it?

Edit: beaten I guess

The1ManMoshPit
Apr 17, 2005

RonniePudding posted:

Is there a way in C++ to compare two byte arrays without cycling through each byte?

memcmp from the c standard library or std::equal from the c++ STL algorithm library (your comparison would be std::equal(bufferOld, bufferOld + BUFFERLENGTH, bufferNew)).

The1ManMoshPit
Apr 17, 2005

Pack 4-character strings into a single 32-bit integer for great justice.

Don't actually do this.

The1ManMoshPit
Apr 17, 2005

Make an algorithm and a heuristic interface (pure abstract class) and write all your code conforming to the interfaces, with both as separate objects. Make factories that can construct the classes by name. Create the two things using the factories. Easily change your algorithm/heuristic at run-time by just swapping the two things out as desired by name.

Decide for yourself whether you want to have the caller store both algorithm and heuristic and pass the heuristic to the algorithm when necessary or whether you just want to create an algorithm and pass it the name of the heuristic on creation and it can then store and manage the heuristic object itself.

The1ManMoshPit
Apr 17, 2005

Compile the pattern to a DFA and then run the DFA on the input :coolfish:

The1ManMoshPit
Apr 17, 2005

rjmccall posted:

The fact that it's an instantiation of std::vector doesn't matter; STL implementations generally do not provide any explicit instantiations of std::vector, because (unlike e.g. std::basic_string<char>) there aren't any obvious instantiations that most programs using the STL will need.

Not that this has anything to do with the problem at hand, but doesn't the STL specialize vector<bool> to use single bits?

The1ManMoshPit
Apr 17, 2005

It's also a bad idea because consider what happens when you do this:

code:
class Tree
{
public:
    Tree() : Child1(this), Child2(this) {}
    virtual void InitLeaf(Leaf* l) {}
};

class CoolTree
{
public:
    virtual void InitLeaf(Leaf* l)
    {
        DoSomethingCool();
    }
};

class Leaf
{
public:
    Leaf(Tree* p)
    {
         p->InitLeaf(this);
    }
};
Hint: what happens isn't good, even if you move the initialize step into the body of the constructor.

The1ManMoshPit
Apr 17, 2005

One of my favourite runtime errors is "Pure Virtual Function Call."

Honestly, that's the better case, because your program crashes and blows up instead of just silently failing in weird and wonderful ways.

The1ManMoshPit
Apr 17, 2005

OneEightHundred posted:

Platform-specific fun stuff: It looks like if you allocate memory using "new", then Visual C++'s debugger will break it down as the type it was allocated as even if referenced through a parent type or void pointer. However, if it was created using a custom allocator (i.e. placement new), it only shows as the type of the pointer you're looking at.

Anyone know if there's a mechanism for producing the type-aware behavior with a custom allocator?

VS can only do this if your class has a v-table, because that's what it uses to determine the type at run-time. It should work no matter where the memory is or how it was allocated as long as it does.

Edit: I just threw together a quick test and verified this myself in VC++ Express 2010;

code:
class Parent
{
public:
    Parent(int i) : a(i) {}
    // comment out the virtual destructor and the runtime info isn't there in either case
    // leave it in and the info shows up in both cases
    virtual ~Parent() {}
private:
    int a;
};

class ChildOfParent : public Parent
{
public:
    ChildOfParent(int i, int j) : Parent(i), b(j) {}
private:
    int b;
};

void FunctionTakingParent(Parent* p)
{
    int i = 1 + 2;  // breakpoint here
}

int main(int argc, const char* argv[])
{
    char buffer[1024];
    Parent* pp = new (buffer) ChildOfParent(1, 2);
    Parent* pq = new ChildOfParent(1, 2);
    FunctionTakingParent(pp);
    FunctionTakingParent(pq);

    return 0;
}

The1ManMoshPit fucked around with this message at 02:45 on Sep 28, 2011

The1ManMoshPit
Apr 17, 2005

HipsLikeCinderella posted:

It appears that arrays and array accesses don't work how I think they work in C. I have a text file with each line in it containing a string of 32 ones and zeros. What I'm trying to do is load them in an array to operate on them later.

Seems I can read them to my storage array, print out the value immediately after reading it in, and everything seems fine. But when I make the same call to access the array only a couple lines down, something goes terribly wrong. I've been banging my head against the wall trying to figure it out but that doesn't seem to be helping much. Any sort of nudge in the right direction would be immensely helpful.

Here's the problem code, if anything more specific like the input file is needed I can provide one.

http://pastebin.com/8c4LsZHY

You're only incrementing i by 1 in the loop, even though you are reading more than 1 byte every iteration, so you are stomping over old data with every read.

Also, why is program defined as a 2d array if you aren't using it like one?

The1ManMoshPit
Apr 17, 2005


Bruce Dawson actually recently posted on AltDevBlogADay that he thought the advice in that paper wasn't all that good and he was going to write a better version of it soon. Unfortunately AltDevBlogADay seems to be down and Gamasutra (who reprinted the article) are protesting SOPA so I can't bring it up, but anyways even though that is often cited the author himself believes there's flaws in it.

It does seem to work very well for me though.

The1ManMoshPit
Apr 17, 2005

I would check for unnecessary copying of things, like functions calls that take Arrays by value instead of reference, that sort of thing. If he has something stupid like this

code:
class Array4D
{
...
    Array3D operator[](int i)
    {
       return Array3D(justGoAheadAndCopyOutASliceOfMyMemory);
    }
};

class Array3D
{
public:
    Array3D()
    {
        malloc(allTheMemoryINeed);
    }
    ~Array3D()
    {
        free(allTheMemoryINeed);
    }
};
it would totally explain everything.

The1ManMoshPit
Apr 17, 2005

It's not impossible or anything; Ada allows you to overload functions by return type. It might be useful sometimes but given how many opportunities there already are to shoot yourself in the foot with unexpected implicit conversions in c++ it's probably better not to have it.

The1ManMoshPit
Apr 17, 2005

That Turkey Story posted:

I don't know, I think that's sort of a mistake in reasoning. We're emulating the functionality here via implicit conversions, since you want some similar behavior in terms of deduction, but it's not really an implicit conversion. For one, it's not implicit, at least not in the way that an implicit conversion is implicit -- you're making an explicit call to get the functionality, so it's not like you can accidentally run into the conversion when you don't want it. Second, you only use the result when it's deduced, otherwise you get an error (and you get an error if it's ambiguous), so it's not like you have an expression of a desired type and it's unexpectedly being converted to something else. You essentially have nothing at all before the deduction takes place.

I'm just not a big fan of implicit conversion in general, IMO the keyword "explicit" shouldn't even exist in c++, "implicit" should instead. The problem with allowing multiple return types is it becomes much harder to reason about which version of foo() is getting called when you assign to a type that requires an implicit conversion, much like it's already hard to reason about what overloaded function gets called. It becomes way harder when you have code like foo(bar()) where both foo & bar have multiple overloads.

I don't think it's impossible and I have found myself thinking "gee it would be useful to be able to overload by return type here" before, but when I think about the implications due to c++'s already sometimes hard to understand function call resolution rules it makes me wary.

quote:

Think of the functionality more like the automatic deduction of template arguments, which is something that we take for granted already. When you call std::for_each, you probably don't think it's unsafe that the template arguments are deduced, right? In other words, you (thankfully) don't have to do:

std::for_each< typename std::vector< T >::const_iterator, some_function_object >( my_T_vector.begin(), my_T_vector.end(), some_function_object() );

Instead, you just pass in the objects, leaving the template arguments to be deduced automatically by the compiler. If we lived in a world where you had to call function templates like the example I posted above, then it would be an error to not specify them when making a call. In the for_each example, what have we lost by automatically deducing the template arguments? In terms of functionality here, nothing. Arguably, someone could say that which instantiation is used may be unclear, though if that really were a problem then there's nothing stopping a programmer from being explicit even though they don't have to. In practice, we generally don't do that because the behavior is already pretty clear.

That kind of stuff can be cool, but here's a favourite counter-example of mine: which constructor gets called for i & which for j, and how many lines of code in the STL is required to make both of these calls do the same thing?

code:
        std::vector<unsigned int> i(1, 2);
        std::vector<unsigned int> j(1u, 2u);
The fact that the STL is designed to hide the unexpected behaviour here is admirable, but it can place an unnecessary burden on developers wanting to create similarly robust code. There are obvious benefits when you finally have a working library, and if it's already made for you all the better, but there are a lot of crazy cases to account for if you want your code to be easy to call into. I imagine allowing overload by return type would just increase the number of workarounds of this type you would need to do.

quote:

A similar thing happens with automatic return type deduction. What happens currently in C++ when you try to do what the original poster was doing? You get an error. If we make it work, are we losing anything? Not really. Again, you can argue, just like with the template argument deduction example, that you are losing clarity, but if you really feel that that is the case, then there's nothing stopping you from being explicit if you want to, you just don't have to be explicit.

And keep in mind that I'm not saying that the return type pattern should be considered when doing resolution for the function template being called. Even in that case, though, I'm not entirely convinced that it would be too horrible, only because I don't imagine that situations where it would be complicated to deduce would be common in practice.

Like I said, I think for sure there are places where it would be useful, but it's hard to try and wrap my brain around the number of crazy cases that would inevitably arise.

The1ManMoshPit
Apr 17, 2005

That Turkey Story posted:

I don't see how this really relates to what we are talking about. Anyway, if you want my personal opinion on the problem there, it's a combination of not having concepts, and IMO the interface could have been better designed.

It was just an example of unexpected behaviour with function overloading and the amount of effort some people have expended to make it work how a reasonable person would expect, rather than how strict typing dictates. I guess it's only related to overload by return type inasmuch as it deals with overloading in general.

quote:

I don't think that's true, at least not with how I've specified them. Show me how automatically deduced return types makes this worse.

I have to admit when I tried to think of a not-totally-contrived example showing how overload by return value would make the situation worse I was stumped. I think I'm just getting old and conservative - I guess it's already annoying when you have to deal with the corner-cases of overload resolution and the thought of adding any more rules to it just generates a knee-jerk reaction.

I mean, the number of contrived examples you can come up with is staggering, but as you say in practice no sane person would ever consider writing code that way. There would certainly be horrible examples of people writing bad code but that's really no different than what we have now.

The1ManMoshPit
Apr 17, 2005

nielsm posted:

(Random annoyance: I've always wondered why std::set and std::map don't have a bool contains(const Key &) const convenience function.)

size_type count(const Key&) const is basically the same thing.

Adbot
ADBOT LOVES YOU

The1ManMoshPit
Apr 17, 2005

qkkl posted:

I only want to write one method though, so if I want to make a change I don't have to do it for two methods.

I ended up using the macro posted before in my float_type.h file so I can pick the type of float I want when compiling. It was better than my original idea because it plays better with version control since I don't have to edit the float_type.h file.

Word to the wise: You're going to end up with rounding errors, in one configuration or the other, in any parts of code that contain floating-point literals. 0.1 != 0.1f != 0.1L.

You can work around this with literal operators in c++11 but you have to remember to always use your custom suffix which is easy to forget.

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