|
In the context of a ray tracer, it's very useful to have Point and Vector be different classes. This lets affine transformations do the right thing automatically by cancelling the translation when transforming a Vector. You will probably also want to define a third type for Normal, since applying transformations to a normal does not affect them in the same way (see http://medialab.di.unipi.it/web/IUM/Waterloo/node45.html).
|
# ? Mar 9, 2013 11:36 |
|
|
# ? Jun 8, 2024 00:22 |
|
I'm re-inventing the header-only linear algebra library wheel. For vectors, I'm storing the data in an array with a template parameter-defined size, instead of separate x,y,z,w fields. The goal here is maximum genericity: I want to use the same class for any vector size, and for both realtime/graphics operations and offline operations with larger vectors. code:
code:
code:
code:
I'm assuming that, since the accessor functions are inline, there shouldn't be a performance problem with this approach. But it doesn't feel very elegant; is there a better way to go about this?
|
# ? Mar 9, 2013 14:24 |
|
What are you gaining by using template-level vector sizes? For 2-, 3-, and 4-vectors it makes sense since you often use them explicitly (although probably you'd be better off using separate classes, especially for matrices) but for large vectors I can't think of a scenario when you'll be explicitly using Vec<1024> rather than having your vector sizes depend on some data from a file or problem specification.
|
# ? Mar 9, 2013 14:51 |
|
Jabor posted:Public inheritance could be useful for interacting with libraries that just represent a Point as a vec3 or whatever, while still getting the benefits of type-safety in your own code. Assuming you can create all your Points yourself, that is - otherwise you'd have to wrap any library functions that return a Point, and then you might as well wrap everything in type-safe versions. The problem with using public inheritance here is that it violates the Liskov substitution principal and also gets rid of many of the benefits the two types being separate in practice. For instance, as was noted, the adding of two points is not a meaningful operation on its own, however, if you use public inheritance the operation becomes valid and results in a vector (since the vector's operator + or "plus" function will match), and similarly, you'd be able to "normalize" a point, which doesn't make sense. There are many other problems that are more difficult to avoid, such as the implicit conversion to vectors through copy and assign operators. Instead, the vector should be a member or you should use private inheritance. In the case of private inheritance, you get the benefit of being able to bring in any valid member function's from the vector. Similarly, there is nothing stopping you from exposing the vector and making it not an implementation detail. I.E. you can have a function called "as_vector" that explicitly returns a reference to the vector (note that this ties the implementation down no more than public inheritance would have) Seven Round Things posted:My solution was to add member functions x(), y(), z(), and w(), which use static_assert to ensure that they're being used on sufficiently large vectors. For example: I also have a little toy library I've been working on on and off for a couple years, but that is not yet production ready (compiler crashes in clang for complicated tensor operations and gcc just completely chokes on almost everything). One of its features offers another solution to your problem. The idea is that instead of x, y, and z, I have the concept of "dimension" types and "index" types (index types are for tensor indices, not indices into a vector). When you declare a tensor type, you specify which dimensions and tensor indices it uses (with appropriate defaults) and it overloads operator[] accordingly. Very briefly, you can do: code:
Anyway, as you know, there are plenty of really good, high quality libraries for this already, which I recommend you use instead unless your end goal is just "make a cool linear algebra library."
|
# ? Mar 9, 2013 18:52 |
|
Volte posted:What are you gaining by using template-level vector sizes? For 2-, 3-, and 4-vectors it makes sense since you often use them explicitly (although probably you'd be better off using separate classes, especially for matrices) but for large vectors I can't think of a scenario when you'll be explicitly using Vec<1024> rather than having your vector sizes depend on some data from a file or problem specification.
|
# ? Mar 9, 2013 18:54 |
|
Fair enough, although if you care about instruction-level optimization you'll probably end up specializing the member functions anyway to provide implementations for the specific small sizes. In the large case though I can't see how having it in the template argument is at all usable. Unless all your vector sizes are known at compile time (which seems unlikely in the general case) the vector class will be useless.
|
# ? Mar 9, 2013 21:59 |
|
Volte posted:In the large case though I can't see how having it in the template argument is at all usable. Unless all your vector sizes are known at compile time (which seems unlikely in the general case) the vector class will be useless. Eigen does this by providing a Eigen::Dynamic which allows you to specify the size at runtime. You can mix and match fixed and dynamically sized dimensions any way you like.
|
# ? Mar 9, 2013 22:20 |
|
rjmccall posted:
Following up on this: I had to look up what this "template with an enum parameter" construction means, because most resources explaining templates seem to only cover templates with types as parameters, but I think I understand now. What if a subset of the possible values for an enum are to be treated differently? Evidently I can do things like this: code:
code:
code:
|
# ? Mar 10, 2013 11:45 |
|
(I'm a little late replying, sorry)Volte posted:What are you gaining by using template-level vector sizes? For 2-, 3-, and 4-vectors it makes sense since you often use them explicitly (although probably you'd be better off using separate classes, especially for matrices) but for large vectors I can't think of a scenario when you'll be explicitly using Vec<1024> rather than having your vector sizes depend on some data from a file or problem specification. Volte posted:Unless all your vector sizes are known at compile time (which seems unlikely in the general case) the vector class will be useless. One advantage of storing the data in an array is that for passing vector data to APIs which expect them to be so. For instance, I can do code:
(Of course, often a struct will store its data contiguously and can therefore be used in the same way, but I can't rely on that due to potential padding and such.) That Turkey Story posted:Other possibilities are that you can just make them non-members, overloaded for the proper size. quote:You can also exploit SFINAE instead of using a static assert in the member function version, although that's a little bit hairy to do correctly. quote:As a final recommendation, I personally don't use x, y, and z at all in the library, but rather, right, up, and forward (and their opposites, with a built-in understanding of opposite).
|
# ? Mar 10, 2013 16:08 |
|
Seven Round Things posted:Would there be any benefit to this, performance or otherwise? roomforthetuna fucked around with this message at 17:33 on Mar 10, 2013 |
# ? Mar 10, 2013 17:29 |
|
roomforthetuna posted:If I'm understanding correctly, I think the benefit would be that it would report as a compile-time error rather than at runtime, if you were to try to use z for a 2D vector. code:
It needs C++11, sure, but I'm no longer willing to support decade-old standards. roomforthetuna posted:Or it might be that you could initialize vectors without explicitly giving their size? code:
|
# ? Mar 10, 2013 18:00 |
|
Seven Round Things posted:Would there be any benefit to this, performance or otherwise? Seven Round Things posted:This initially sounds attractive, but wouldn't it cause confusion when passing the vectors to APIs of different handedness? Seven Round Things posted:I did originally try to go for a more SFINAE approach but I couldn't make it work without lots of annoying and ugly code duplication. Here is a direct link to the header. There are brief docs and tests in there as well. It requires boost. https://svn.boost.org/svn/boost/sandbox/enable_if/include/boost/utility/enable_if_macros.hpp code:
Also, I've only run the tests with clang and gcc. I imagine Microsoft's compiler may choke on it, so run the tests before you use it if you're using VC++. Edit: Oh yeah, and it requires the VMD library from the sandbox too, forgot about that. Probably too much for you to pull in if all you want to do is enable a member function. That Turkey Story fucked around with this message at 19:02 on Mar 10, 2013 |
# ? Mar 10, 2013 18:45 |
|
That Turkey Story posted:So you may as well just use that if you don't want to pull in dependencies or voodoo preprocessor and template metaprogrammed stuff That Turkey Story posted:Forward/back etc
|
# ? Mar 10, 2013 21:00 |
|
Isn't enable_if part of std now for easy SFINAE stuff, if so why not use it here? I used it for the first and so far only time about a month ago for something vaguely similar (automated endian swapping on suitable types for composing network packet structs). I don't remember the details but do you end up having to explicitly specify the function template type parameter or something annoying like that? I made mine private and called them with <T> from public wrapper methods, which made the public interface a bit easier to read anyway.
|
# ? Mar 10, 2013 21:47 |
|
Rottbott posted:Isn't enable_if part of std now for easy SFINAE stuff, if so why not use it here? I used it for the first and so far only time about a month ago for something vaguely similar (automated endian swapping on suitable types for composing network packet structs). I don't remember the details but do you end up having to explicitly specify the function template type parameter or something annoying like that? I made mine private and called them with <T> from public wrapper methods, which made the public interface a bit easier to read anyway.
|
# ? Mar 10, 2013 22:00 |
|
tts could you give an explanation for how the named arguments work in your tensor example?
|
# ? Mar 10, 2013 22:43 |
|
FamDav posted:tts could you give an explanation for how the named arguments work in your tensor example? The quickest way to do it is probably to use Boost.Parameter (the docs are pretty good there so I won't explain it here). For what I'm doing, it'd be difficult/impossible to use for various reasons (my tensors are variadic, I allow initialization by "left" for "right," and I use the names for more things than just named parameter passing such as for indexing, swizzling, etc.). If people are really curious I might just upload everything. There's nothing too complicated about it other than the fact that it's a bit of metaprogramming. C++11 constructor delegation also helps a lot.
|
# ? Mar 10, 2013 23:04 |
|
That Turkey Story posted:std::enable_if and the boost enable if in trunk are difficult to use in certain situations (variadic constructors, conversion operators, when the condition isn't dependent on a parameter of the template being instantiated as in the z() case here, and expressions, etc.). I wrote up a little blurb in the boost docs that details some of this and the contortions you need to do to make things work -- the macros do all of that behind the scenes and more, such as making it easy to specify multiple, named lazy types. code:
The really tricky part of this example though is thinking of a good name other than 'Vector' for a vector, to avoid confusion with std::vector. Less of a problem when you have Vector2, Vector3 etc.
|
# ? Mar 10, 2013 23:59 |
|
Rottbott posted:I see, I tried with std::enable_if and had to do quite a bit of hoop-jumping: That doesn't look like it should compile. The easiest way to do it with std::enable_if would be: code:
|
# ? Mar 11, 2013 00:13 |
|
That Turkey Story posted:The quickest way to do it is probably to use Boost.Parameter (the docs are pretty good there so I won't explain it here). For what I'm doing, it'd be difficult/impossible to use for various reasons (my tensors are variadic, I allow initialization by "left" for "right," and I use the names for more things than just named parameter passing such as for indexing, swizzling, etc.). If people are really curious I might just upload everything. There's nothing too complicated about it other than the fact that it's a bit of metaprogramming. C++11 constructor delegation also helps a lot. Thanks! And it would be interesting to look through if you ever do upload it.
|
# ? Mar 11, 2013 00:23 |
|
Hammerite posted:Following up on this: I had to look up what this "template with an enum parameter" construction means, because most resources explaining templates seem to only cover templates with types as parameters, but I think I understand now. There's no subtyping in enums, so it's not as easy as "overloading" the class template at different enum types, but if the enum ranges don't overlap, then you can just do partial specialization on value: C++ code:
You'll probably want to explicitly inherit constructors from FooBase (if you can use C++11).
|
# ? Mar 11, 2013 01:55 |
|
Good news everybody, we finally got rid of Boost.Program_options from our code. Having to specify two versions of options, one for the code and one to get sane help output, wasn't bad enough to get us to stop using it -- the way various versions of the library would break compatibility with certain platforms we were targeting wasn't even sufficient. Nor were the surprise behavior it exhibited in how it parsed the command line, or the bizarre treatment of certain flags passed at the end of a command line, sufficient reason. Actually, I forget the reason -- that was somebody else's problem. I think it had to do with option-specific help output, maybe related to having to maintain two separate versions of options and getting exceptions from the wrong version. Anyway it took about 1 day of work to cleanly replace, far less than the time that had been wasted dealing with that yet another entry in a long list of overengineered libraries.
|
# ? Mar 11, 2013 02:52 |
|
shrughes posted:Good news everybody, we finally got rid of Boost.Program_options from our code. Having to specify two versions of options, one for the code and one to get sane help output, wasn't bad enough to get us to stop using it -- the way various versions of the library would break compatibility with certain platforms we were targeting wasn't even sufficient. Nor were the surprise behavior it exhibited in how it parsed the command line, or the bizarre treatment of certain flags passed at the end of a command line, sufficient reason. I hate well-tested and useful things too. Let's just write everything from scratch and call it better because of it!!!
|
# ? Mar 11, 2013 03:08 |
|
That Turkey Story posted:I hate well-tested and useful things too. Let's just write everything from scratch and call it better because of it!!! Silly me, here I was looking for a library that didn't provide negative value.
|
# ? Mar 11, 2013 07:07 |
|
$ rm -rf boost_spirit/
|
# ? Mar 11, 2013 07:23 |
|
I came across C99's addition of the static type qualifier for array declarators:C99 Standard section 6.7.5.3.7 posted:A declaration of a parameter as ‘‘array of type’’ shall be adjusted to ‘‘qualified pointer to This seems kind of weird for a couple of reasons.
|
# ? Mar 11, 2013 08:43 |
|
Rothon posted:I came across C99's addition of the static type qualifier for array declarators: Not sure why they went a different route this time on whether to add a new keyword, but you can get some possible optimisations out of it. Off the top of my head: 1. It lets the compiler assume that the argument isn't NULL, so it can skip checks for that (which might be pulled in from inline functions or whatever). 2. If it knows they exist, it can load elements of the array into memory before the program asks for them (eg pulling them into a vector register, or just reordering normal instructions for better performance). Without a guarantee that the array was big enough, it'd risk a page fault.
|
# ? Mar 11, 2013 09:05 |
|
That Turkey Story posted:That doesn't look like it should compile. The easiest way to do it with std::enable_if would be: However your example doesn't: 'default template arguments are only allowed on a class template'. Passing the argument explicitly works fine, so the wrapper method does seem to be required. (I'm not sure what I messed up that std::enable_if wouldn't work for me with a non-type parameter before.)
|
# ? Mar 11, 2013 09:21 |
|
I don't see why this compiles (with clang++ -Weverything -std=c++11 -stdlib=libc++):C++ code:
|
# ? Mar 11, 2013 12:48 |
|
the <x.h> headers have to declare them in the global namespace, and the <cx> headers have to declare them in the std namespace, but either one might declare them in both. If you want to be able to definitely for sure use them without specifying std:: then you need to use <x.h>, but I've never heard of a compiler library that doesn't import into the global namespace in the <cx> headers. As far as not polluting the global namespace, just don't include them in a header. There's not really such a thing as "polluting" the namespace in a cpp file since it doesn't bleed into any other files.
|
# ? Mar 11, 2013 13:12 |
|
Volte posted:the <x.h> headers have to declare them in the global namespace, and the <cx> headers have to declare them in the std namespace, but either one might declare them in both. If you want to be able to definitely for sure use them without specifying std:: then you need to use <x.h>, but I've never heard of a compiler library that doesn't import into the global namespace in the <cx> headers. As far as not polluting the global namespace, just don't include them in a header. There's not really such a thing as "polluting" the namespace in a cpp file since it doesn't bleed into any other files. The <cxxxx> headers aren't supposed to act as if you've put using namespace std, at least according to the site linked. So the example shouldn't be compiling since he doesn't have std::printf, just printf. That said I think you are right, and the site linked is wrong..
|
# ? Mar 11, 2013 17:18 |
|
Rottbott posted:On MSVC 2010 mine compiles and works, but knowing MSVC maybe it shouldn't! I will have to check some of my real code against Clang at some point. Are you sure? Did you just compile the template or did you test it with vector sizes that would support "Z". I can't even imagine Microsoft's compiler would accept it since you're never passing template arguments to GetZ() even though it takes an explicit template argument (it can't possibly know that U is supposed to be the size of the vector). To be clear, it would compile the template, but the Z() member function would never produce a valid instantiation. Also, since you're not doing SFINAE at the top-level you'd be gaining nothing over static_assert. Just use static_assert. Rottbott posted:However your example doesn't: 'default template arguments are only allowed on a class template'. Passing the argument explicitly works fine, so the wrapper method does seem to be required. (I'm not sure what I messed up that std::enable_if wouldn't work for me with a non-type parameter before.) That's because your compiler doesn't support default arguments for function template parameters yet. They were added in C++11.
|
# ? Mar 11, 2013 17:22 |
|
xgalaxy posted:The <cxxxx> headers aren't supposed to act as if you've put using namespace std, at least according to the site linked. So the example shouldn't be compiling since he doesn't have std::printf, just printf. They definitely are not allowed to act as if they contained a using directive for namespace std. They are, however, allowed to declare things in the global namespace as well as the std namespace. This is generally done with using declarations (using std::printf;) rather than using directives (using namespace std;). It is honestly enough of a struggle sometimes to get system header maintainers to put #ifdef __cplusplus\nextern "C"\n#endif in all of their headers. Expecting them to put a namespace std { in there so that the C++ library can "override" that header to inject a list of using declarations is just not going to happen. The global namespace has a lot of crap in it. Try not to put the C++ standard library's crap there, too; otherwise, just live with it.
|
# ? Mar 11, 2013 19:47 |
|
On the topic of const, would a constant pointer to an array of pointers to constant data look something like - const Foo** const data; I just got onto this topic in my course and was curious on how to write this up. My professor didn't have time to explain how to do it so I figured someone here could answer.
|
# ? Mar 12, 2013 07:46 |
|
fallnomega posted:On the topic of const, would a constant pointer to an array of pointers to constant data look something like - Yeah. The variable is the on the right of the asterisk, and the target type is on the left of the asterisk. So there's a "const data" to the right of the rightmost asterisk, meaning that the variable 'data' cannot be changed. 'data' points at a 'const Foo*'. The target of that `const Foo*` is a Foo that cannot be changed.
|
# ? Mar 12, 2013 08:01 |
|
fallnomega posted:On the topic of const, would a constant pointer to an array of pointers to constant data look something like - Yeah, you read pointer declarations right to left. const Foo * volatile * const *foo is a pointer a constant point to a volatile pointer to a Foo, which is const. Incidentally, const Foo * and Foo const * have the same meaning, so it's basically a religious debate as to which one to use. Foo const * and Foo * const are different though, (pointer to constant foo, and constant pointer to Foo respectively).
|
# ? Mar 12, 2013 08:12 |
|
I've started following the 'new style' Foo const instead of const Foo in the last year. It took a little while to get used to it, but I now prefer it.
|
# ? Mar 12, 2013 08:18 |
|
That Turkey Story posted:Are you sure? Did you just compile the template or did you test it with vector sizes that would support "Z". I can't even imagine Microsoft's compiler would accept it since you're never passing template arguments to GetZ() even though it takes an explicit template argument (it can't possibly know that U is supposed to be the size of the vector). To be clear, it would compile the template, but the Z() member function would never produce a valid instantiation. In my class where I am using this technique (not a vector) the reason for using SFINAE is that there are multiple private methods with identical signatures. With static_assert they would all exist at once which wouldn't compile. Otherwise I'd prefer static_assert for the clear error message. That Turkey Story posted:That's because your compiler doesn't support default arguments for function template parameters yet. They were added in C++11.
|
# ? Mar 12, 2013 11:01 |
|
I'm trying to get a quickSort with a median-of-three partition method working, and I am having a heck of a time with it. I just can't seem to logically figure out how to get the median-of-three thing to work. Here's the code. This thing sorts just fine, but takes twice as long when compared to other quicksort code that I found online (but can't use), so it leads me to believe my median-of-three function isn't working at all.
|
# ? Mar 14, 2013 22:32 |
|
|
# ? Jun 8, 2024 00:22 |
|
jeeves posted:I'm trying to get a quickSort with a median-of-three partition method working, and I am having a heck of a time with it. I just can't seem to logically figure out how to get the median-of-three thing to work. Instead of trying to directly swap the array elements (which confuses things because you're renaming stuff partway through), try working out what the median-of-3 element is and then returning it. Returning the value being looked for is generally a better idea than modifying your parameters in a weird, undocumented way in order to communicate something back. Something like this: code:
As a bonus, it would be a good idea to actually use the constant you've defined for the median of three threshold instead of ignoring it and hard-coding the value anyway.
|
# ? Mar 14, 2013 22:57 |