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
astr0man
Feb 21, 2007

hollyeo deuroga

C89 posted:

If either operand is negative, whether the result of the / operator is the largest integer less than the algebraic quotient or the smallest integer greater than the algebraic quotient is implementation-defined, as is the sign of the result of the % operator.

Adbot
ADBOT LOVES YOU

floWenoL
Oct 23, 2002

Hammerite posted:

I was under the impression that given integers a and b (b not zero), if you write

code:
q = a / b
r = a % b
then you're guaranteed that

  • q*b + r will be equal to a
  • The absolute value of r will be less than that of b
  • If a and b are both positive then r will be nonnegative.

I want to do integer division like this (calculating q) except that I want it to always be guaranteed that r will be nonnegative, regardless of the signs of a and b.

As far as I can tell, the code I posted does this, but maybe there is something more you know that I do not (it has been my impression that C/++ has many "gotchas" that can trip programmers up).

Ah, I see what you're trying to do. However, the function as posted is buggy: SensibleIntegerDivision(-5, -3) gives 0, where it should give +2 (if I understand you correctly).

Edit:
-2 should be +2. Thanks, Jabor!

floWenoL fucked around with this message at 02:43 on Feb 16, 2013

floWenoL
Oct 23, 2002

astr0man posted:

If either operand is negative, whether the result of the / operator is the largest integer less than the algebraic quotient or the smallest integer greater than the algebraic quotient is implementation-defined, as is the sign of the result of the % operator.

That is true, although you're leaving out the part where (a/b)*b + a%b is supposed to equal a. In that case, it's possible to write code that does what Hammerite wants regardless of the implementation.

Jabor
Jul 16, 2010

#1 Loser at SpaceChem

floWenoL posted:

Ah, I see what you're trying to do. However, the function as posted is buggy: SensibleIntegerDivision(-5, -3) gives 0, where it should give -2 (if I understand you correctly).

By the looks of what he's asking, it should be +2 in that case.

It looks like he wants to subtract sign(b) (so add 1 when the denominator is negative, subtract 1 when it's positive) instead of always subtracting 1. I think.

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe
That's very embarrassing. The thing I had been using it for only used positive b (in fact, b = 2), so that's why I didn't notice any wrong results.

Cool Blue Reason
Jan 7, 2010

by Lowtax
I need a way to pass a program some integers with execlp. I've tried using sprintf to convert the numbers before I send them but it just kept giving me segmentation faults (I think this is just cause I didn't make room for the null terminating character but I'm not able to try it out right now), and I don't have access to itoa. Are there any better ways to go about this? Sprintf doesn't really seem ideal but from what I'm hearing casting an int to a char* in C isn't ideal anyway.

astr0man
Feb 21, 2007

hollyeo deuroga
Post your code? sprintf shouldn't segfault. Also you should use snprintf instead of sprintf.

jiggerypokery
Feb 1, 2012

...But I could hardly wait six months with a red hot jape like that under me belt.

If i am trying to overload the [] operator in a vector how do I go about it?

At present I have two typedefs

code:
typedef std::vector<bool> Sequence;
typedef std::vector<Sequence> Pattern;
Pattern is a vector of vectors of bools.

I am trying to overload pattern[] to return the number of true values at each position.

EG

If pattern looked like
1 0 1 1
1 1 0 0
1 0 1 1
1 0 1 0

pattern[3] would return 2 by means of trivial for loop.

Is it possible to do this in a fashion that means it only happens when accessed out side of the class so within the class the operator is not overloaded? Perhaps by using some kind of namespace malarky?

raminasi
Jan 25, 2005

a last drink with no ice

jiggerypokery posted:

If i am trying to overload the [] operator in a vector how do I go about it?

At present I have two typedefs

code:
typedef std::vector<bool> Sequence;
typedef std::vector<Sequence> Pattern;
Pattern is a vector of vectors of bools.

I am trying to overload pattern[] to return the number of true values at each position.

EG

If pattern looked like
1 0 1 1
1 1 0 0
1 0 1 1
1 0 1 0

pattern[3] would return 2 by means of trivial for loop.

Is it possible to do this in a fashion that means it only happens when accessed out side of the class so within the class the operator is not overloaded? Perhaps by using some kind of namespace malarky?

You can't change the interfaces of other classes (for good reason - it would be confusing as hell). The two ways to do what you want are actually to create a Pattern class with an overloaded operator [] or just create a free function int whateverTheOperationNameIs(const Pattern & p) (this way you don't get to use operator [], but what you're describing isn't an accepted thing that operator [] does so it would be considered bad practice to use it that way).

jiggerypokery
Feb 1, 2012

...But I could hardly wait six months with a red hot jape like that under me belt.

Makes sense, I suspected as much. Thanks very much!

(Still new to this!)

ashgromnies
Jun 19, 2004
I haven't programmed C++ for 8 years and was just a teenager when I did, now I hopped on to a project again and I'm fine because I've done a fair amount of pure C in the meanwhile but I can't figure out how to read this constructor definition(the class FingerPosition is a inherits from both Control and Note):

code:
FingerPosition(midi_bodypart_index hi, midi_bodypart_index fi, double pos) : Control(hi, fi, pos), Note(hi, fi, pos) {}
What do the parent constructor declarations on the right-hand side of the colon imply?

tractor fanatic
Sep 9, 2005

Pillbug
In the definition of a constructor, after the function signature and before the opening brace, you can put a color and then a bunch of initializers, which will call constructors for member classes and base classes. You need to use initializer lists because after the opening brace every member and base will already have been constructed. The constructors are called in the order of their declaration as members (I forgot what the order for the base classes is, but regardless, this isn't something you should depend on because it gets very confusing).

rjmccall
Sep 7, 2007

no worries friend
Fun Shoe

tractor fanatic posted:

The constructors are called in the order of their declaration as members (I forgot what the order for the base classes is, but regardless, this isn't something you should depend on because it gets very confusing).

Virtual bases, direct or not (if this is the ultimate type being constructed; otherwise, these are ignored), followed by non-virtual direct bases in the order they were specified as bases, followed by fields in order of their declaration in the class. The virtual base order is basically a depth-first post-order traversal, again in order of specification.

Bases before fields and all in their original order is right enough in common cases. But yeah, it's usually poor style to rely on this.

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe
I can't work out what is wrong with this code.

main.cpp:

code:
#include "wrapper-application.h"

int main (int argc, char* argv []) {
    hexDrod hd();
    wrapperApplication wa(&hd);
    return wa.run();
}
It results in this error:

error: no matching function for call to 'wrapperApplication::wrapperApplication(hexDrod (*)())'

Yet if I change the first line to "hexDrod hd;" it compiles fine. I can't understand the error message and I can't see anything wrong with this code. Surely I'm just invoking the constructor for the hexDrod class? I have declared the constructor and all that jazz, and as far as I can see I got the syntax right.

wrapper-application.h:

code:
#ifndef WRAPPER_APPLICATION_H
    #define WRAPPER_APPLICATION_H

    #include <SDL.h>
    #include "hex-drod.h"

    class wrapperApplication {
        protected:
            bool running;
            hexDrod* hd;
            SDL_Surface* displaySurface;
            screenData currentScreenData;
            keyRepeatData currentKeyRepeatData;
            bool startup ();
            void shutdown ();
            void handleEvent (SDL_Event* Event);
        public:
            wrapperApplication (hexDrod* myHexDrod);
            int run ();
    };
#endif
hex-drod.h:

code:
#ifndef HEX_DROD_H
    #define HEX_DROD_H

    #include <SDL.h>
    #include "configuration-messages.h"
    #include "application-constants.h"
    #include "graphics-constants.h"
    #include "gameplay-constants.h"

    class hexDrod {
        public:
            hexDrod ();
            bool startup ();
            // declarations of other functions...
    };
#endif
hex-drod.cpp:

code:
#include "hex-drod.h"

hexDrod::hexDrod () {}

bool hexDrod::startup () {
    return true;
}

// other functions...

Nippashish
Nov 2, 2005

Let me see you dance!

Hammerite posted:

It results in this error:

error: no matching function for call to 'wrapperApplication::wrapperApplication(hexDrod (*)())'

Yet if I change the first line to "hexDrod hd;" it compiles fine. I can't understand the error message and I can't see anything wrong with this code. Surely I'm just invoking the constructor for the hexDrod class?

When you write hexDrod hd(); you're actually declaring a function named hd that takes no arguments and returns a hexDrod. That's why the compiler thinks you're trying to pass a function pointer to wrapperApplication's constructor.

Acer Pilot
Feb 17, 2007
put the 'the' in therapist

:dukedog:

e: the guy above me has a way better explanation

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe
Oh ok. So instead I should write the following (which compiles)?

code:
int main (int argc, char* argv []) {
    hexDrod hd = hexDrod();
    wrapperApplication wa(&hd);
    return wa.run();
}

Jewel
May 2, 2009

Why not just hexDrod hd;? That still invokes the default constructor, I'm pretty sure.

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe

Jewel posted:

Why not just hexDrod hd;? That still invokes the default constructor, I'm pretty sure.

Originally I had hexDrod* hd; wrapperApplication(hd);

I noticed that the compiler gave a warning that hd was "used uninitialised", and then I twigged that that code does not actually create a hexDrod. It just declares a pointer to one, so if my application actually did anything as opposed to (at this stage) being an empty shell, I might see some bizarre behaviour. But I guess now that it is no longer declared as a pointer, this caution is no longer necessary.

Jewel
May 2, 2009

Hammerite posted:

Originally I had hexDrod* hd; wrapperApplication(hd);

I noticed that the compiler gave a warning that hd was "used uninitialised", and then I twigged that that code does not actually create a hexDrod. It just declares a pointer to one, so if my application actually did anything as opposed to (at this stage) being an empty shell, I might see some bizarre behaviour. But I guess now that it is no longer declared as a pointer, this caution is no longer necessary.

Yeah, basically this:

C++ code:
hexDrod hd;
This means "Make a new hexDrod stored in the variable 'hd', and initialise it with a default constructor."

C++ code:
hexDrod hd(36, 85);
This means "Make a new hexDrod stored in the variable 'hd', and initialise it with paramaters.

C++ code:
hexDrod* hd;
This means "Make a new pointer called 'hd' to a hexDrod object". This pointer is uninitialized and contains garbage.

C++ code:
hexDrod* hd = 0;
Same as above, except pointer is essentially nulled. Now you can check later "if (hd) { //Do Stuff }" to see if hd has been set to something. Very useful. Always initialize pointers to 0 (or NULL depending on the compiler) if they're going to be set later.

C++ code:
hexDrod* hd = new hexDrod; //Maybe hexDrod() works here, no idea.

/*Can also be written as:
hexDrod* hd;
hd = new hexDrod;*/
This is the same as above, but it now creates a new hexDrod object on the heap, and points 'hd' to its memory address.
Because it's on the heap and not the stack, this will not be auto-deleted and you must 'delete hd;' yourself when done with it. Anything you use "new" for you HAVE to delete it too.

C++ code:
hexDrod hd;
hexDrod* hdPointer = &hd;
hd.doWhatever();
hdPointer->doWhatever();
This creates a new hexDrod object on the stack, which will autodelete itself when it falls out of scope.
It then creates a new pointer to a hexDrod object and points it to the memory address of 'hd'.
Because you didn't use 'new' here you don't have to delete anything.

Hopefully this helped! I'm not too good at this myself but this always helps me think about stuff. Please correct me if I'm wrong! Also this was all freehanded so hopefully I didn't forget something.

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe

Jewel posted:

Hopefully this helped! I'm not too good at this myself but this always helps me think about stuff. Please correct me if I'm wrong! Also this was all freehanded so hopefully I didn't forget something.

Thanks, this confirms what I thought but I also learned something about "new" and having to delete things that are allocated that way.

I am puzzling a bit over how to structure the code in this project. I want to do things "the right way" so I end up with code that can be followed easily. I have the "wrapper application" which contains the main event loop, and event handling code that just determines the type of an event and calls individual event handlers in the hd child object. But I'm sort of just using the child object as a glorified namespace and I wonder whether it's a bit unnecessary to do it that way.

Update: I realised that inheritance is the way to do this after all, rather than having member objects tied in to the object's functionality.

Hammerite fucked around with this message at 12:15 on Feb 19, 2013

Plorkyeran
Mar 22, 2007

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

Nippashish posted:

When you write hexDrod hd(); you're actually declaring a function named hd that takes no arguments and returns a hexDrod. That's why the compiler thinks you're trying to pass a function pointer to wrapperApplication's constructor.
Note that this particular bit of weirdness is known as the most vexing parse.

jiggerypokery
Feb 1, 2012

...But I could hardly wait six months with a red hot jape like that under me belt.

Is it possible to make data structures require initialisation i.e have some kind of constructor? Or do I need to make PatternPreset a class in order to do that?

code:
struct PatternPreset {
    /*
     Size of arrays initialised as follows;
        Keypoints[necklaces]
        Pulses[necklaces][depth]
        breakEarly[necklaces][depth]
     */
    
    int necklaces = 0;      //The number of necklaces to form one sequence
    int depth = 0;          //The number of sequences to form one pattern
    
    int*    keyPoints = nullptr;    //The length of each of the necklaces
    int**   pulses = nullptr;       //The number of pulses in each of the necklaces in the pattern
    bool**  breakEarly = nullptr;   //Which necklaces to break early
};

seiken
Feb 7, 2005

hah ha ha
There's no difference between structs and classes in C++ except that structs have default visibility public and classes have default visibility private.

By convention structs are used for "plain old data" types and classes for more complicated things but it's just preconceived notions, it's not enforced by the language in any way.

Edit: technically structs/classes also default to public/private inheritance respectively when not specified, though I've never really seen this come up in practice.

seiken fucked around with this message at 16:01 on Feb 19, 2013

jiggerypokery
Feb 1, 2012

...But I could hardly wait six months with a red hot jape like that under me belt.

So they actually all do have default constructors, you just don't have to actually type one out? That's good to know, thanks!

Also I have commented an error I am getting with my constructor.

Why is this a problem? Is there any work around?

code:

struct PatternPreset {
    /*
     Size of arrays initialised as follows;
        Keypoints[necklaces]
        Pulses[necklaces][depth]
        breakEarly[necklaces][depth]
     */
    PatternPreset(int necklaces, int depth){
        this->necklaces = necklaces;
        this->depth = depth;
        
        keyPoints = new int[necklaces];
        pulses = new int [necklaces][depth];
        breakEarly = new int[necklaces][depth]; //Error here. Only the first dimension may have dynamically allocated size what do?
    }
    
    int necklaces = 0;      //The number of necklaces to form one sequence
    int depth = 0;          //The number of sequences to form one pattern
    
    int*    keyPoints = nullptr;    //The length of each of the necklaces
    int**   pulses = nullptr;       //The number of pulses in each of the necklaces in the pattern
    bool**  breakEarly = nullptr;   //Which necklaces to break early
};

jiggerypokery fucked around with this message at 16:08 on Feb 19, 2013

Gul Banana
Nov 28, 2003

it's a problem because the memory layout of arrays is determined largely at compile time; there's a special allowance made for 1d arrays with a runtime-determined size.
workaround is to not use raw arrays. prefer std::vector or perhaps boost::multi_array

jiggerypokery
Feb 1, 2012

...But I could hardly wait six months with a red hot jape like that under me belt.

I am a little confused. If i was to use the vector class, could I make a vector of 1d arrays who's size is dynamically allocated? If so how would that look?

tractor fanatic
Sep 9, 2005

Pillbug
If you just want to be able to do pulses[i][j] then the easiest way is to make a vector of vectors, like std::vector<std::vector<int>>. However, this means you have to make a loop through the outer vector to set the lengths of each internal vector, and it's more overhead than is necessary since you don't need a jagged array. It also means two dereferences instead of one. If you don't want to use boost, the best way is to make a one dimensional array of your choosing, and make some helper functions to convert a two dimensional index into a one dimensional index.

roomforthetuna
Mar 22, 2005

I don't need to know anything about virii! My CUSTOM PROGRAM keeps me protected! It's not like they'll try to come in through the Internet or something!

jiggerypokery posted:

I am a little confused. If i was to use the vector class, could I make a vector of 1d arrays who's size is dynamically allocated? If so how would that look?
A vector is essentially a 1d array whose size is dynamically allocated, so you'd probably want to make a vector of vectors.
std::vector<std::vector<whateveritcontains>> the_object;

It's likely that "whateveritcontains" would be some sort of pointer because that way there can be subclasses and stuff, which is often what people want with their 2D arrays. You can't do std::vector<baseclass> for this because whatever child class is usually larger than a baseclass, and such a vector only allocates sizeof(baseclass) per element.

nielsm
Jun 1, 2009



Don't use arrays at all. Default to use a std::vector unless you can give a thorough explanation for why it's unsuited for your situation.

Gul Banana
Nov 28, 2003

roomforthetuna posted:

It's likely that "whateveritcontains" would be some sort of pointer because that way there can be subclasses and stuff, which is often what people want with their 2D arrays. You can't do std::vector<baseclass> for this because whatever child class is usually larger than a baseclass, and such a vector only allocates sizeof(baseclass) per element.

also because virtual methods only provide runtime polymorphism when indirected!
this:

vector<baseclass>[0].methodcall()

would slice the object, calling baseclass::methodcall on its "base part", rather than subclass::methodcall

That Turkey Story
Mar 30, 2003

nielsm posted:

Don't use arrays at all. Default to use a std::vector unless you can give a thorough explanation for why it's unsuited for your situation.

I disagree with this, but it probably doesn't matter much in practice. Use an array or a std::array unless the size is dynamic or if it is both large and you are putting it directly onto the stack.

nielsm
Jun 1, 2009



That Turkey Story posted:

I disagree with this, but it probably doesn't matter much in practice. Use an array or a std::array unless the size is dynamic or if it is both large and you are putting it directly onto the stack.

Right, I should have added the qualifier "for dynamic allocations" or something like that.

seiken
Feb 7, 2005

hah ha ha
You could always just do
C++ code:
breakEarly = new int[necklaces * depth];
and index with
code:
i * depth + j

jiggerypokery
Feb 1, 2012

...But I could hardly wait six months with a red hot jape like that under me belt.

Thanks so much for all the help guys. Some really helpful stuff.

I think the bottom line is the whole situation needed rethinking. Trying to brute force a solution isn't really the way to go. (I do like seikens approach despite the costs of readability).
Going on what you guys have said about vectors being a better option for dynamic memory stuff i've gone with a vector of nested structures, and improved readability a whole lot.

code:
struct PatternPreset {
    
    PatternPreset(int necklaces, int depth){
        this->necklaces = necklaces;
        this->depth = depth;
    }
    
    struct NecklacePreset{
        NecklacePreset(int intervals, int pulses, bool BreakEarly = false){
            this->intervals = intervals; //should be a small ish number say <20. Maybe should add some protection against big numbers
            this->pulses = pulses;          //HAS to be < Intervals. 
            this->BreakEarly = BreakEarly;
        }
        
        int m(){ return intervals;}
        int p(){ return pulses;}
        bool breakEarly() {return BreakEarly;}
 
    private:
        int intervals  = 0;
        int pulses = 0;
        bool BreakEarly;
    };
    
    int x(){return necklaces;}
    int d(){return depth;}
    
    std::vector<NecklacePreset> N; //Size = necklaces * depth (x * d).
    
private:
    int necklaces;      //The number of necklaces to form one sequence
    int depth;          //The number of sequences to form one pattern
};
I can't be bothered to add the value safeguards at this time. I might do if I have time towards the end of the project.

jiggerypokery fucked around with this message at 19:48 on Feb 19, 2013

nielsm
Jun 1, 2009



Some suggestions:

First is your constructors. You should make a habit of using initializer lists for everything possible, mostly because you will need to use them anyway when you start using types that need complex initialization as class members, but also because it's just the C++ way of doing it.

C++ code:
struct PatternPreset {
    PatternPreset(int necklaces, int depth)
    : necklaces(necklaces), depth(depth)
    {
    }
};
Since it isn't really clear from that, the form is field_name(initial_value). If the field is of a type with a constructor then the initializer is a call to that type's constructor, like thing(5, y).


Second, since the vector of presets has a complex access scheme (it's not just straight indices) and since the users of the class should not (as far as I can tell) be able to change the number of elements anyway, I would hide it as a private member and provide an access function.

C++ code:
struct PatternPreset {
  NecklacePreset & n(size_t x, size_t d) { return N[x + d * this->x()]; }
private:
  std::vector<NecklacePreset> N;
};
The n() function returns a (non-const) reference to a NecklacePreset, which allows both direct modification of the indexed object as well as even assignment:

C++ code:
PatternPreset p = ...;
std::cout << p.n(4, 7).m();
p.n(2, 1) = PatternPreset::NecklacePreset(8, 3, false);

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe
Which is the best approach out of these two:

Approach 1: global const bool arrays that may be checked against

code:
#ifndef MATHEMATICS_FUNCTIONS_H
    #define MATHEMATICS_FUNCTIONS_H

    #include <math.h>

    extern const bool forceArrowMovementPermissions[7][6];
    extern const bool stepBlockerMovementPermissions[3][6];

    int iRoundAwayFromZero (float f);
        // Note: do not attempt to use this function to round floats that are
        // very large (in absolute value), as it may give inaccurate results.
        // "Very large" here means numbers whose integer part is 15+ digits.
#endif
code:
#include "mathematics-functions.h"

const bool forceArrowMovementPermissions[7][6] = {
    {true , true , false, false, false, true }, // Force arrow pointing north
    {true , true , true , false, false, false}, // Force arrow pointing northeast
    {false, true , true , true , false, false}, // Force arrow pointing southeast
    {false, false, true , true , true , false}, // Force arrow pointing south
    {false, false, false, true , true , true }, // Force arrow pointing southwest
    {true , false, false, false, true , true }, // Force arrow pointing northwest
    {true , true , true , true , true , true }  // No force arrow present on hex
};

const bool stepBlockerMovementPermissions[3][6] = {
    {false, true , false, true , false, true }, // Step blocker preventing positive-axis movement
    {true , false, true , false, true , false}, // Step blocker preventing negative-axis movement
    {true , true , true , true , true , true }  // No step blocker present on hex
};

int iRoundAwayFromZero (float f) {
    // Please see the note in the header file re: the limitations of this function.
    return (int)(f > 0 ? floor(f + 0.5) : ceil(f - 0.5));
}
Approach 2: define global functions to perform these checks?

code:
#ifndef MATHEMATICS_FUNCTIONS_H
    #define MATHEMATICS_FUNCTIONS_H

    #include <math.h>

    #define FORCE_ARROW_LAYER_EMPTY  6
    #define STEP_BLOCKER_LAYER_EMPTY 2

    bool forceArrowLayerPermitsMovement (const int forceArrowLayer, const int movementDirection);
    bool stepBlockerLayerPermitsMovement (const int stepBlockerLayer, const int movementDirection);

    int iRoundAwayFromZero (float f);
        // Note: do not attempt to use this function to round floats that are
        // very large (in absolute value), as it may give inaccurate results.
        // "Very large" here means numbers whose integer part is 15+ digits.
#endif
code:
#include "mathematics-functions.h"

bool forceArrowLayerPermitsMovement (const int forceArrowLayer, const int movementDirection) {
    return
        forceArrowLayer == FORCE_ARROW_LAYER_EMPTY ||
        (7 - forceArrowLayer + movementDirection) % 6 < 3;
}

bool stepBlockerLayerPermitsMovement (const int stepBlockerLayer, const int movementDirection) {
    return
        stepBlockerLayer == STEP_BLOCKER_LAYER_EMPTY ||
        (bool)((stepBlockerLayer + movementDirection) % 2);
}

int iRoundAwayFromZero (float f) {
    // Please see the note in the header file re: the limitations of this function.
    return (int)(f > 0 ? floor(f + 0.5) : ceil(f - 0.5));
}

Hammerite fucked around with this message at 07:30 on Feb 20, 2013

greatZebu
Aug 29, 2004

Hammerite posted:

Which is the best approach out of these two:
Approach 1: global const bool arrays that may be checked against
Approach 2: define global functions to perform these checks?

When in doubt, prefer exposing functions to exposing data. You can always define your functions in terms of lookups on static arrays under the hood. You might also want an assert or some error handling in your rounding function to back up your "no really big numbers" comment.

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe

greatZebu posted:

When in doubt, prefer exposing functions to exposing data. You can always define your functions in terms of lookups on static arrays under the hood. You might also want an assert or some error handling in your rounding function to back up your "no really big numbers" comment.

So something like this, you would say? (NB. I have changed the behaviour of the second one to allow both kinds of movement restrictions at once.)

code:
#ifndef MATHEMATICS_FUNCTIONS_H
    #define MATHEMATICS_FUNCTIONS_H

    #include <assert.h>
    #include <math.h>

    int iRoundAwayFromZero (float f);
        // This function asserts that its argument is less than 10^16 in
        // absolute value. This is because the method it uses to perform
        // rounding may give inaccurate results for floats that are very large
        // in absolute value.

    bool forceArrowPermitsMovement (const int forceArrowDirection, const int movementDirection);
    bool stepBlockerPermitsMovement (const int stepBlockerTypes, const int movementDirection);
        // These functions perform unchecked array dereferencing (against
        // appropriate static const arrays) using the supplied integer
        // arguments. forceArrowDirection should be in the range 0 to 6
        // inclusive; stepBlockerTypes should be in the range 0 to 3 inclusive;
        // and in each case movementDirection should be in the range 0 to 5
        // inclusive. Each function asserts that these requirements are
        // satisfied.
#endif
code:
#include "mathematics-functions.h"

int iRoundAwayFromZero (float f) {
    assert(f > -1e16);
    assert(f <  1e16);
    return (int)(f > 0 ? floor(f + 0.5) : ceil(f - 0.5));
}

bool forceArrowPermitsMovement (const int forceArrowDirection, const int movementDirection) {
    static const bool movementPermitted[7][6] = {
        {true , true , false, false, false, true }, // Force arrow pointing north
        {true , true , true , false, false, false}, // Force arrow pointing northeast
        {false, true , true , true , false, false}, // Force arrow pointing southeast
        {false, false, true , true , true , false}, // Force arrow pointing south
        {false, false, false, true , true , true }, // Force arrow pointing southwest
        {true , false, false, false, true , true }, // Force arrow pointing northwest
        {true , true , true , true , true , true }  // No force arrow present on hex
    };
    assert(forceArrowDirection >= 0);
    assert(forceArrowDirection <  7);
    assert(movementDirection   >= 0);
    assert(movementDirection   <  6);
    return movementPermitted[forceArrowDirection][movementDirection];
}

bool stepBlockerPermitsMovement (const int stepBlockerTypes, const int movementDirection) {
    static const bool movementPermitted[4][6] = {
        {true , true , true , true , true , true }, // No step blocker present on hex
        {false, true , false, true , false, true }, // Step blocker preventing positive-axis movement
        {true , false, true , false, true , false}, // Step blocker preventing negative-axis movement
        {false, false, false, false, false, false}  // Both types of step blockers are present
    };
    assert(stepBlockerTypes  >= 0);
    assert(stepBlockerTypes  <  4);
    assert(movementDirection >= 0);
    assert(movementDirection <  6);
    return movementPermitted[stepBlockerTypes][movementDirection];
}

Adbot
ADBOT LOVES YOU

jiggerypokery
Feb 1, 2012

...But I could hardly wait six months with a red hot jape like that under me belt.

Ok yeah, The first part makes perfect sense. I have always used initialiser lists for classes that have lots of other things going on in the constructor and not if the constructor would be empty other wise. Personally I find it more readable but if that is bad form i'll switch to your way from now on.

The second part I am a little confused about.

nielsm posted:

Second, since the vector of presets has a complex access scheme (it's not just straight indices) and since the users of the class should not (as far as I can tell) be able to change the number of elements anyway, I would hide it as a private member and provide an access function.

C++ code:
struct PatternPreset {
  NecklacePreset & n(size_t x, size_t d) { return N[x + d * this->x()]; }
private:
  std::vector<NecklacePreset> N;
};
The n() function returns a (non-const) reference to a NecklacePreset, which allows both direct modification of the indexed object as well as even assignment:

C++ code:
PatternPreset p = ...;
std::cout << p.n(4, 7).m();
p.n(2, 1) = PatternPreset::NecklacePreset(8, 3, false);

Would calling the n accessor function not allow modification of the elements anyway?

If it was;

code:
const NecklacePreset & n(size_t x, size_t d) { return N[x + d * this->x()];
Would that make all the values in it read only?

Maybe I just need to read about constants because I often just want a variable you can only assign once, but you can not assign constants ever. Correct?

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