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
Volte
Oct 4, 2004

woosh woosh
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).

Adbot
ADBOT LOVES YOU

Seven Round Things
Mar 22, 2010
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:
template <typename T, unsigned int N>
class Vec
{
  public:
    T data[N];
    Vec<T,N>& operator+=(const Vec<T,N>& other)       {for (unsigned int i = 0; i < N; i++) {data[i] += other.data[i];} return *this;}
    // etc
}
The obvious disadvantage here is that one can't access the fields like so:
code:
float blah = myVec.x;
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:
code:
inline T w() { static_assert(N >= 4, "It does not make sense to call w() for a 2- or 3-element vector."); return data[3]; }
inline Vec<T,N>& w(T newValue) { static_assert(N >= 4, "It does not make sense to call w() for a 2- or 3-element vector."); data[3] = newValue; return *this; }
And one may use
code:
float blah = myVec.x()
instead.
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?

Volte
Oct 4, 2004

woosh woosh
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.

That Turkey Story
Mar 30, 2003

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:
code:
inline T w() { static_assert(N >= 4, "It does not make sense to call w() for a 2- or 3-element vector."); return data[3]; }
inline Vec<T,N>& w(T newValue) { static_assert(N >= 4, "It does not make sense to call w() for a 2- or 3-element vector."); data[3] = newValue; return *this; }
And one may use
code:
float blah = myVec.x()
instead.
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?
Other possibilities are that you can just make them non-members, overloaded for the proper size. 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.

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:
// A rank 1 tensor with x, y, z, w dimensions and a default tensor index
// (some somehwhat complicated preprocessor and template metaprogramming)
typedef TENSOR( double, ( x, y, z, w ) ) vector3;

// initialize components by name (some slightly complicated metaprogramming)
vector3 my_vector( y = 6.0, x = 1.0, z = 2, w = 1 );

// Access components by name (some basic metaprogramming)
my_vector[x] = 3.0;
There's a lot more to the library, but this aspect of it is something that isn't too difficult to get running for anyone's tensor types, especially if all you want is the indexing of components. 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). I recommend this since it gets rid of some common confusion, such as whether or not the 3rd dimension goes forward or back. You'd be surprised how easy it makes reading the code as well by simply using "right" and "left" instead of "x".

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."

That Turkey Story
Mar 30, 2003

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.
I'm going to disagree here. Even if you just use size 2, 3, and 4 vectors, I think it's beneficial to use a template. It's less to write, test, and maintain, and is not really much if at all more difficult to implement.

Volte
Oct 4, 2004

woosh woosh
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.

Nippashish
Nov 2, 2005

Let me see you dance!

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.

Hammerite
Mar 9, 2007

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

rjmccall posted:

code:
enum Axis {X,Y,Z,A,B,C};
template <Axis> class Coord {
  ...
};
typedef XCoord Coord<X>;
...

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:
enum axis {x, y, z, a, b, c};
enum majorAxis {x, y, z};
enum minorAxis {a, b, c};

template <axis> class classP {...};
template <majorAxis> class classQ {...};
template <minorAxis> class classR {...};
But can I then do

code:
template <axis> class toBeSpecialCased {...};
template <minorAxis> class toBeSpecialCased {...};
or even

code:
template <majorAxis> class toBeTreatedDifferently {...};
template <minorAxis> class toBeTreatedDifferently {...};
What if minorAxis is declared with explicit values to match those in axis, i.e. as enum(a = 3, b = 4, c = 5)?

Seven Round Things
Mar 22, 2010
(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.
To be realistic, vectors with 2 >= size <= 4 are all I'm likely to use. It was just the cleanness and lack of code duplication I was really going for; I'll forget about large and dynamic-size vectors.
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:
something.addVector(myVec.data);
instead of having to build a temporary. OpenGL expects this, as I understand.
(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.
Problem there is that, for example, z() is valid for any vector with size >= 3, so I don't want to have it available only for Vec3s.

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.
Would there be any benefit to this, performance or otherwise?

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).
This initially sounds attractive, but wouldn't it cause confusion when passing the vectors to APIs of different handedness?

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!

Seven Round Things posted:

Would there be any benefit to this, performance or otherwise?
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. Or it might be that you could initialize vectors without explicitly giving their size? I don't know, I should have just let the person who said it explain what they meant, now that I've reconsidered I regret posting this comment but there is no delete, noooo!

roomforthetuna fucked around with this message at 17:33 on Mar 10, 2013

Seven Round Things
Mar 22, 2010

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.
It does already, static_assert is compile-time:
code:
inline T z() {static_assert(N >= 3, "SCPGM: It does not make sense to call z() for a 2-element vector."); return data[2]; }
inline Vec<T,N>& z(T newValue) {static_assert(N >= 3, "SCPGM: It does not make sense to call z() for a 2-element vector."); data[2] = newValue; return *this; }

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?
That would be nice, true, but it doesn't bother me too much. I have typedefs for things like Vec2f, Vec2i, Vec2u etcetera:
code:
auto myVec = Vec2f(2.4, 6.3);
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.

That Turkey Story
Mar 30, 2003

Seven Round Things posted:

Would there be any benefit to this, performance or otherwise?
Both will be fine and easy to optimize. The main benefit of exploiting SFINAE is that it makes instantiation of the function impossible when it doesn't make sense rather than producing an error upon instantiation. Because of this, if you take the SFINAE approach, you can use a call to the function when forming other expressions that are to be used for exploiting SFINAE. If this is not clear/important in your case, then don't worry about it.

Seven Round Things posted:

This initially sounds attractive, but wouldn't it cause confusion when passing the vectors to APIs of different handedness?
Actually, this is one of the main reasons to do it (it's the motivating case). The idea is that if you are using a left-handed coordinate system you use "forward" and when you use a right-handed coordinate system you use "back," as opposed to the ambiguous "z." Since the relationship between forward and back is known, the library can easily convert between them when necessary. Similarly, if you access the "back" coordinate of a vector that stores "forward" you get a negative proxy to the element, meaning that in most cases you don't have to think about right/left-handedness at all, until you go and pass the data off to a low-level API. This applies to higher-rank tensors as well.

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.
What problem were you having? The only tricky part here is that in order for SFINAE to work you need the condition/expression to be dependent on a template parameter of the template for which substitution is currently taking place. If you are using C++11, I wrote a family of macros that makes this easy and have them up in the boost sandbox if you want to try it out.

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:
template< class ScalarType, std::size_t NumComponents >
class vector
{
public:
  template< BOOST_ENABLE_IF_C( NumComponents >= 3 ) >
  ScalarType& z() { return m_components[2]; }

  // ...
private:
  ScalarType m_components[NumComponents];
};
Anyway, for this case, it won't get you much over a static_assert, so you may as well just use that if you don't want to pull in dependencies or voodoo preprocessor and template metaprogrammed stuff, but I've come to greatly prefer SFINAE over static asserts in library code due to composability.

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

Seven Round Things
Mar 22, 2010

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
Yeah, I really don't! But I'll bear those macros in mind- they're exactly what I was trying to do.

That Turkey Story posted:

Forward/back etc
That does sound useful, put that way. I'll probably end up keeping x/y/z() for idiomatic compatibility with other things expecting a vector, but I'll consider adding some form of handedness-awareness. Thanks!

Rottbott
Jul 27, 2006
DMC
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.

That Turkey Story
Mar 30, 2003

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.
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.

FamDav
Mar 29, 2008
tts could you give an explanation for how the named arguments work in your tensor example?

That Turkey Story
Mar 30, 2003

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.

Rottbott
Jul 27, 2006
DMC

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.
I see, I tried with std::enable_if and had to do quite a bit of hoop-jumping:

code:
template<typename T, size_t NbElements>
struct Vector
{
	T Z() const { return GetZ(); }

private:

	static const bool ZAllowed = NbElements > 2;

	template<size_t U>
	typename std::enable_if<Vector<T, U>::ZAllowed, T>::type GetZ() { return Data[3]; }

	T Data[NbElements];
};
Maybe that way the public interface could end up marginally easier to read, but BOOST_ENABLE_IF_C looks like something I should be using. I've only recently started letting a little TMP sneak into my code (or rather, the fruits of other people doing TMP), for fear of upsetting coworkers etc.

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.

That Turkey Story
Mar 30, 2003

Rottbott posted:

I see, I tried with std::enable_if and had to do quite a bit of hoop-jumping:

code:
template<typename T, size_t NbElements>
struct Vector
{
	T Z() const { return GetZ(); }

private:

	static const bool ZAllowed = NbElements > 2;

	template<size_t U>
	typename std::enable_if<Vector<T, U>::ZAllowed, T>::type GetZ() { return Data[3]; }

	T Data[NbElements];
};
Maybe that way the public interface could end up marginally easier to read, but BOOST_ENABLE_IF_C looks like something I should be using. I've only recently started letting a little TMP sneak into my code (or rather, the fruits of other people doing TMP), for fear of upsetting coworkers etc.

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.

That doesn't look like it should compile. The easiest way to do it with std::enable_if would be:
code:
template< class T, std::size_t Size >
class vector
{
public:
  template< std::size_t S = Size >
  typename std::enable_if< S >= 3, T& >::type z()
  {
    return m_components[2];
  }
  // ...
};

FamDav
Mar 29, 2008

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.

rjmccall
Sep 7, 2007

no worries friend
Fun Shoe

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.

What if a subset of the possible values for an enum are to be treated differently? Evidently I can do things like this:

code:

enum axis {x, y, z, a, b, c};
enum majorAxis {x, y, z};
enum minorAxis {a, b, c};

template <axis> class classP {...};
template <majorAxis> class classQ {...};
template <minorAxis> class classR {...};

But can I then do

code:

template <axis> class toBeSpecialCased {...};
template <minorAxis> class toBeSpecialCased {...};

or even

code:

template <majorAxis> class toBeTreatedDifferently {...};
template <minorAxis> class toBeTreatedDifferently {...};

What if minorAxis is declared with explicit values to match those in axis, i.e. as enum(a = 3, b = 4, c = 5)?

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:

template <Axis axis> class FooBase {
  // shared stuff goes here
};

template <Axis axis, bool isMajorAxis = (axis <= Axis::Z)> class Foo;
template <Axis axis> class Foo<axis, true> : FooBase<axis> {
  // major axis implementation goes here
};

template <Axis axis> class Foo<axis, false> : FooBase<axis> {
  // minor axis implementation goes here
};

You'll probably want to explicitly inherit constructors from FooBase (if you can use C++11).

shrughes
Oct 11, 2008

(call/cc call/cc)
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.

That Turkey Story
Mar 30, 2003

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.

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.

I hate well-tested and useful things too. Let's just write everything from scratch and call it better because of it!!!

shrughes
Oct 11, 2008

(call/cc call/cc)

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.

Suspicious Dish
Sep 24, 2011

2020 is the year of linux on the desktop, bro
Fun Shoe
$ rm -rf boost_spirit/

Rothon
Jan 4, 2012
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
type’’, where the type qualifiers (if any) are those specified within the [ and ] of the
array type derivation. If the keyword static also appears within the [ and ] of the
array type derivation, then for each call to the function, the value of the corresponding
actual argument shall provide access to the first element of an array with at least as many
elements as specified by the size expression.

This seems kind of weird for a couple of reasons.
  1. Why was the static keyword reused for this? It doesn't seem like a particularly appropriate choice. C99 added the restrict keyword, so it's not like the new standard was limited to what already existed in C89.
  2. How is the guarantee to the compiler that an argument will have a certain number of elements useful to the compiler? What checking does the compiler have to add if static is left out? I didn't think that the compiler even had a notion of array capacity in this sense.

Qwertycoatl
Dec 31, 2008

Rothon posted:

I came across C99's addition of the static type qualifier for array declarators:


This seems kind of weird for a couple of reasons.
  1. Why was the static keyword reused for this? It doesn't seem like a particularly appropriate choice. C99 added the restrict keyword, so it's not like the new standard was limited to what already existed in C89.
  2. How is the guarantee to the compiler that an argument will have a certain number of elements useful to the compiler? What checking does the compiler have to add if static is left out? I didn't think that the compiler even had a notion of array capacity in this sense.

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.

Rottbott
Jul 27, 2006
DMC

That Turkey Story posted:

That doesn't look like it should compile. The easiest way to do it with std::enable_if would be:
code:
template< class T, std::size_t Size >
class vector
{
public:
  template< std::size_t S = Size >
  typename std::enable_if< S >= 3, T& >::type z()
  {
    return m_components[2];
  }
  // ...
};
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.

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.)

Deus Rex
Mar 5, 2005

I don't see why this compiles (with clang++ -Weverything -std=c++11 -stdlib=libc++):

C++ code:
#include <cstdio>

int main(void)
{
    printf("%d\n", 5);
    return 0;
}
I thought that the <cxxxxx> system headers were supposed to declare those functions in the std namespace, as outlined here, while the <xxxxx.h> headers did not to retain backwards compatibility. I want to use some functions declared in the system C headers, but I also don't want to pollute the global namespace with them.

Volte
Oct 4, 2004

woosh woosh
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.

xgalaxy
Jan 27, 2004
i write code

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..

That Turkey Story
Mar 30, 2003

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.

:confused: 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.

rjmccall
Sep 7, 2007

no worries friend
Fun Shoe

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.

fallnomega
Sep 20, 2002
*drivez da mothershit outta a plane on beer*
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.

shrughes
Oct 11, 2008

(call/cc call/cc)

fallnomega posted:

On the topic of const, would a constant pointer to an array of pointers to constant data look something like -

const Foo** const data;

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.

b0lt
Apr 29, 2005

fallnomega posted:

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.

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).

xgalaxy
Jan 27, 2004
i write code
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.

Rottbott
Jul 27, 2006
DMC

That Turkey Story posted:

:confused: 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.
Oops, my posted code was not quite the version I tested. The GetZ() call should indeed be GetZ<NbElements>, and there was a const missing. With those it works as expected. Also I now realise why putting the bool constant directly into the enable_if didn't work - my condition was > 2 instead of >= 3, which was closing the template. It was late...

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.
Aha. Good, I look forward to being able to use that.

jeeves
May 27, 2001

Deranged Psychopathic
Butler Extraordinaire
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.

Adbot
ADBOT LOVES YOU

Jabor
Jul 16, 2010

#1 Loser at SpaceChem

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.

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.

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:
if (A[left] > A[middle]) {
  if (A[left] < A[right]) {
    return A[left];
  } else if (A[middle] > A[right]) {
    return A[middle];
  } else {
    return A[right];
  }
} else {
  // And similarly here
  ...
If you want an example of where your current code falls down, try looking at the case where A[left] is the median value, but A[middle] is the largest - your code ends up swapping A[middle] into the rightmost position.

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.

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