Jump to content

  • Log In with Google      Sign In   
  • Create Account

Banner advertising on our site currently available from just $5!


1. Learn about the promo. 2. Sign up for GDNet+. 3. Set up your advert!


Brother Bob

Member Since 26 Nov 2001
Offline Last Active Today, 05:19 PM

#5211970 Cannot initialize constant base member value

Posted by Brother Bob on 20 February 2015 - 01:33 PM

You need to initialize the base class members from the base class, you cannot do it from the derived classes.

class Block : public Entity
{
public:
Block() : Entity(eIDError) {}
Block(EntityIDs id) : Entity(id) {}
};



#5211960 Cannot initialize constant base member value

Posted by Brother Bob on 20 February 2015 - 01:04 PM

Constant members can only be initialized in the constructor's initializer list, but there's no constructor in your base class to initialize it. Add a constructor that takes an ID so that the base class can initialize its member and call that base constructor from the derived classes.

class base
{
public:
base(__int32 ID) : ID(ID) {}
const __int32 ID;
};



#5211873 Easiest way to pass a 2D array?

Posted by Brother Bob on 20 February 2015 - 06:18 AM

Pay closer attention to where the ampersand goes: clicky.

 

In any case, I suggest some dynamic array anyway. A two-dimensional array, or any-dimensional for the matter, can always be flattened into a one-dimensional storage so there's no need for vectors of vectors of vector of... how many dimensions you decide to have.

std::vector<int> storage(size_x * size_y);
...
int value = storage[y*size_x + x];

Wrap that up in a small class containing the storage and the dimensions, and provide some convenience function to access the storage based on two indices.




#5210820 Exclusive maximum in random functions

Posted by Brother Bob on 15 February 2015 - 08:20 AM

But you can turn that argument around and ask, why would one design the random function's range only to index stuff in arrays smoothly when it's just as likely to be used for simulating die rolls? Stated differently, I *am* designing for general use, and I get the feel that exclusive maximum is a weirdness designed for indexing arrays, a very specific problem.

If you are designing a random generator for die rolls, then go ahead and use an inclusive range. If you design a random generator for array access, then go ahead and use an exclusive range. But a general-purpose random generator is not specifically designed for any case but to generate random numbers, it is designed neither for die rolls nor array accesses.

 

As Bacterius stated earlier, or at least hinted at, when you apply specific use cases to random number generators you are mixing two things; randomness and distributions. A random number generator should preferably only generate numbers, while specific code is used to shape the random numbers into specific distributions. In this case, a die roll and array indices are two distributions.

 

The general way to think about a die rolls that separates randomness and distribution is: I want 6 different random numbers, and once I have them I map them to the possible outcomes of a die roll. If your random function returns numbers between 0 and 5, then your die roll map becomes f(x) = x+1 (that is, insert random number 0 to 5, and get a die roll from 1 to 6), while the array index becomes f(x) = x. Similarly, if your random generator returns a value between 1 and 6, the map becomes f(x) = x for the die roll and f(x) = x-1 for array access.

 

None of the mapping functions are inherently better than the other. However, when you look at many different use cases, the math of 0-based exclusive ranges falls out prettier without magic numbers more often than not. You rarely have to add or subtract 1 to compensate for exclusive ranges.




#5210495 Variable id mapping

Posted by Brother Bob on 13 February 2015 - 09:26 AM

Another suggestion is to store the value as string and perform a lexical cast on demand. In that case, you don't have to determine the type of the data in the parser. For example, is the quoted string "42" supposed to be an integer, a floating point value without a decimal part, or an actual string that just happens to contain numbers? If your parser can always determine that, then that's fine, but maybe it's more useful if the user determines what the type of a certain named variable should be.

struct NamedVariable
{
    std::string name;
    std::string value;
 
    template<typename T>
    T get() {
        std::stringstream ss(value);
        T v;
        ss >> v;
        return v;
    }
};

You can also specialize this for, for example, T=std::string so you just return the value instead of passing it through a string stream object.

 

The benefit of this approach is that you can read your named variables and query them as many different types. You string "42" will be returned as the integer value 42 if you ask for an integer, the floating point value 42.0 if you ask for a float, and the string "42" if you ask for a string, and so on. Any type that can be read from a text stream can be stored and parsed.




#5210279 Qt datatypes with GLM and OpenGL

Posted by Brother Bob on 12 February 2015 - 09:00 AM

The quick non-standard solution, and the solution I'm not going to mention, is to just cast your pointers to GLM-data to Qt-data of the corresponding type. All reasonable vector and matrix libraries are just wrappers on top of the same physical layout; a contiguous memory buffer of floating point values. So, I said it anyway, even though I intended not to. Just don't take it as a long-term solution but something you can do under some control/supervision until you migrate to the Qt solution if that is what you're going for.

 

However, looking at the documentation, there are variants taking either individual X/Y/Z/W-parmeters for vectors, or effectively taking pointers to matrix data (not as plain pointers, but as pointer-to-arrays types).




#5209695 Operator Overloading C++

Posted by Brother Bob on 09 February 2015 - 04:30 PM

But that code isn't "nothing", it is "something". That "something" has side effects (it prints some text), which means that your program is doing different things whether there is an extra copy or not (either prints some additional text or doesn't print some additional text), and the compiled can't know if that is important or not to you personally.

 

The mere fact that you print something means that there is now an observable difference between making the extra copy and not. That may, as SiCrane suggested, very well be the deciding factor for the compiler's ability and/or desire to remove the extra copy. And that could be a problem for you; attempts you make to observe if this extra copy is made or not, could actual end up being the reason why it is made.




#5209606 Operator Overloading C++

Posted by Brother Bob on 09 February 2015 - 09:16 AM

For operators like operator+ and operator+=, why is it that when it comes to operator+= we return a reference? Where with the operator+ we return a copy of a temporary object? Why don't they both return the same things, either both return a copy or a reference?

The + operator creates a new object from two parameters, while the += operator modifies one of the operands. I'll answer this partly by asking you a questions back: If operator + returns a reference, what should it return a reference to? Keep in mind while answering this that, in the syntax c=a+b, you are responsible for creating c from a and b, and neither a nor b shall be modified inside the operator. Try implementing operator + by returning a reference.

 

The += operator is different in that in the syntax a+=b, the object a is modified by b inside the operator. Nothing has to be returned in this case, not even a reference, but a reference is commonly returned in order to chain operators or have access to the modified value directly in the same expression.

 

 

Also I have a few questions when it comes to the copy assignment operator:
MyObject &MyObject::operator=(const MyObject &obj)

1. Why is there a destructor called when there is no local object being created in the code? What is actually being destroyed here?

Use the debugger to break where the destructor is actually called to identify which object is destructed where. Unless I misunderstand you, you are right that no object should be destructed in this case.

 

 

2. When I was reading up on copy assignment operators, I saw that I should have the self-check guard in order to prevent memory leaks and dangling pointers when it comes to objects that use dynamic memory allocation. But I'm wondering do I need this if I know my class is not going to have any dynamic memory stuff? Or is this something I should do just as a "good practice"?

You could do it for performance reasons as well; if the object is expensive to copy (may include other reasons than just dynamic resources) then it may be faster to check for self-assignment than to blindly make an unnecessary self-copy. It all depends on your use case, if you do self-assignment often enough and whether it is worth checking for it or not. 

 

 

3. When read about this operator and dynamic memory allocation, the topic of the "copy swap" idiom comes about. Should I only follow this idiom when dealing with dynamic memory, because an exception could get thrown when trying to allocate data? Or is this something I should just practice as well?

As far as I understand, copy-and-swap is mostly for exception safety reasons. If your assignment cannot fail, then copy-and-swap serves no additional purpose for exception safety. This is, again, not necessarily tied to dynamic resources.

 

Self-assignment and copy-and-swap are tools you should be aware of and know how and when to use, and equivalently know when they aren't needed. Nothing breaks if you use them when it's not needed, but sometimes the tools or some logically equivalent protection is necessary; for example, if self-assignment breaks your objects, you can either add a self-assignment tests or re-structure your program or algorithms such that self-assignment never happens in the first place.




#5208577 Unity basic Script question

Posted by Brother Bob on 04 February 2015 - 02:55 AM

The variable number is a function parameter, and you gave it a value by passing myInt to the function when you called MultiplyByTwo.




#5207823 Low Level Memory Management C++

Posted by Brother Bob on 31 January 2015 - 04:10 AM

You're doing memory allocations with the new and delete operators, not the operator new() and operator delete() functions as I mentioned. Observe the difference in terminology: the new operator is the operator that allocates memory and constructs objects, while the function operator new() is the function used to allocate the memory.

ptr = operator new(sizeof(T) * 5);
new (ptr) T();
&ptr[0]->~T();
operator delete(ptr);

The function operator new() doesn't take any type to allocate because it doesn't work with types and constructed objects, as opposed to the new operator that has to know what type to allocate and construct.




#5207818 Low Level Memory Management C++

Posted by Brother Bob on 31 January 2015 - 03:35 AM

If you're asking how to recreate std::vector with separate memory allocations and object constructions, then you'll do the following:

  1. Allocate memory with the operator new() or malloc() function.
  2. Construct individual objects with placement new.
  3. Destruct objects by calling their destructor.
  4. Release memory with the operator delete() or free() function.

If you're specifically asking about point 3, then destroying an object is as simple as calling the ~T() member function on it (replace T with the class name, or template type parameter you use). Look here for some example memory management: clicky.




#5206767 Why is this taboo?

Posted by Brother Bob on 26 January 2015 - 03:33 PM

This was a double post with replies. The two threads have been merged.




#5206684 Constructor gotcha's?

Posted by Brother Bob on 26 January 2015 - 06:01 AM

One thing that tripped me up in the early days of learning c++ was calling virtual functions in constructors.

Do not call virtual functions in constructors, as they will not work properly!

 

happy.png

Depends on what you mean by "not work properly" though. Calling virtual functions in constructors is well defined and well behaved by the language, but may not be obvious or work how you initially expect it work. I suspect that the problem was a lack of understanding of what will happen, rather than it "not working properly".




#5206361 Constructor gotcha's?

Posted by Brother Bob on 24 January 2015 - 04:15 AM

 


the order of initialization in constructor initializer lists is the *member declaration order*, not the order in which members are lexically arranged in the initialization list.

 

so if i have member variables:

int a;

int b;

 

and in my constructor i say:

b=5;

a=b*2;

 

it will process a=b*2 first?

 

don't recall if i heard of that one before or not. then again, i first learned c++ syntax when it first came out, and haven't really used it much since. so maybe its just yet another thing that i've already forgotten.


 

No, that is assignment but Josh is talking about initialization and the initializer list.

struct foo {
    foo() : b(5), a(b*2) {}
 
    int a;
    int b;
};

In this class, a is declared before b and therefore a is also initialized with twice the value of b, before b has been initializer. Code in the constructor is executed top-down, but that code is executed after the initializer list.




#5206171 Textured cube with glDrawElements

Posted by Brother Bob on 23 January 2015 - 04:47 AM

A cube with more attributes than just position ultimately has 24 unique vertices. If you build the sides of the cube using triangles, you have to generate a total of 32 vertices. However, the shared edge of the two triangles per side also share unique vertices so out of the 32 vertices you need to generate there are only 24 unique ones.

 

So you can still gain something by using indexed primitives because some of the triangles share unique vertices. On the other hand, a cube is close to the worst case for indexed primitives because it only has sharp edges with discontinuous vertex attributes that prevents vertex sharing. Compare to smooth surfaces, such as a sphere, where attributes typically flow smoothly across the surface most of the time.






PARTNERS