Jump to content

  • Log In with Google      Sign In   
  • Create Account

Brother Bob

Member Since 26 Nov 2001
Online Last Active Today, 12:09 PM

#5213538 General questions about matrix multiplications

Posted by Brother Bob on 28 February 2015 - 01:46 PM

Your first two points are correct, but the remaining ones doesn't make any sense. Row/column majorness of a matrix has nothing to do with matrix maths, what you can do with it and what the result of operations are. The majorness affects one and only one thing: how the two-dimensional grid of numbers representing the matrix is mapped to a one-dimensional linear memory storage. You have to store the matrix in some way in one-dimensional memory, and the majorness dictates where the individual matrix elements are stores in memory.

#5212300 glDrawElements (SEGAULT)

Posted by Brother Bob on 22 February 2015 - 12:32 PM

The vector object itself is just a small class that stores a few pointers, such as a pointer to the actual data you put into it, and some internal book-keeping information for things such as how much memory has been allocated, how much is actually used, and so on. You want the pointer to the data stored by the vector, not a pointer to the vector object itself. A pointer to the vector object just points to its internal private data.

#5212266 glDrawElements (SEGAULT)

Posted by Brother Bob on 22 February 2015 - 08:47 AM

You're passing pointers to the vectors to glBufferData, not the pointer to the data the vector contains. Also, pass the vectors by reference instead and skip the size parameter; the vector know its own size.

GLuint InitializeVBOs(std::vector<Vertex> &vertices)
    GLuint VBOid;
    glGenBuffers(1, &VBOid);
    glBindBuffer(GL_ARRAY_BUFFER, VBOid);
    glBufferData(GL_ARRAY_BUFFER, vertices.size()*sizeof(Vertex), vertices.data(), GL_STATIC_DRAW);
    return VBOid;

#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
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
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();
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.