Thursday, February 26, 2009

[C++] Returning Vector from Function

[C++] Returning Vector from Function
Code:
std::vector foo()
{
std::vector v;
/* fill it */
return v; // (1)
}

void bar()
{
std::vector v = foo(); // (2)
v = foo(); // (3)
}
What really happens here?

First off, despite the apparent notation line (2) really is a copy constructor, and not a default constructor + assignment. This is mandated by the standard, and that's the reason why I used both examples (2) and (3).


So...
(2) call foo
(1) copy constructor
(back to 2) copy constructor again (ouch!)

(3) call foo
(1) copy constructor
(back to 3) assignment operator, which is usually implemented as a copy constructor (ouch again!) + swap

So as you can see, the problem is that returning an object will trigger a copy. Most modern compilers have an optimization called "Return Value Optimization" (RVO for short) which is most of the time able to merge the steps (1) and (back to...). But as with any compiler optimization you shouldn't really count on that...


That leaves us with at least one copy, which could easily be avoided:
Code:
void foo(std::vector& retVal)
{
std::vector v;
/* fill it */
retVal.swap(v); // (1)
}

void bar()
{
std::vector v; // (2)
foo(v); // (3)
}
Now we're left with:
(2) default constructor, quite lightweight to say the least
(3) call foo
(1) swapping both vectors, again hyper lightweight
(back to 3) nothing more



So as yabbadabbadont put it, the best way to pass big objects back from the callee to the caller obviously is to use a reference argument.
Although this doesn't really matter for small datasets, returning vectors or other containers from a function can quickly introduce huge overhead when the dataset grows (since it involves copying the whole dataset at least once).


Which leads me way off topic:
A class supporting copy / assignment should always have a swap() method too...
- the copy constructor just does its job
- the swap method quickly and safely (as in nothrow-guarantee) exchanges the contents of two objects
- the assignment operator is implemented in terms of copy + swap (after checking for self-assignment of course)

This way you end up with an exception-safe assignment operator for almost no effort, and many use cases can be optimized away thanks to the swap() method. It's a win-win...

Sunday, February 22, 2009

Detect iPhone browser

How do I detect an iPhone user coming to my site?

Amazon.com's jumped on the wagon, and Meebo has too: they automatically give you a different version of their site if you're browsing from an Apple iPhone. I want to do that too. How the heck do I detect that a visitor has an iPhone?


Dave's Answer:

The key to detecting a browser type is to remember that there's an environment full of information transmitted with each query sent from the web browser to the server. It includes the obvious things like the requested data file

(which can be an HTML document, GIF or JPEG image, AVI movie, FLV animation or related) and the IP address of the browser's computer, but it also includes a critical variable called HTTP_USER_AGENT.

Visit with a regular browser from a regular computer and you'll see something like this:

HTTP_USER_AGENT=Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 2.0.50727)

Here you can see that I'm testing from Firefox (which identifies itself as Mozilla for historical reasons) running on Windows XP (though it's identifying it as Windows NT for some cockamainie reason) and that I'm also running .NET.

Grab that same environment value from a slick Apple iPhone query, though, and the values are quite a bit different:

HTTP_USER_AGENT=Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en)
AppleWebKit/420+ (KHTML, like Gecko) Version/3.0 Mobile/1C25 Safari/419.3

Pretty interesting reading if you're a geek: You can see where it's also identifying itself as Mozilla, but this time as Mozilla/5.0 rather than Mozilla/4.0. The string "iPhone" appears, which is what we'll key on, but notice also that it says that it's on a system with a "CPU like Mac OS X", just as we iPhone users have been suspecting for a while.

You can see the basic test we'll need to do here to ascertain if the visitor is using an iPhone or not: if their HTTP_USER_AGENT environment value contains the string "iPhone", we've got a match. Otherwise it's something else on some other device or computer.

You can do this test and act on it in a zillion different places, ranging from the actual Web server ruleset to JavaScript code to PHP conditional code to even having a separate "gateway" script page that everyone hits and redirects them to the iPhone or non-iPhone version of your Web page and site.

A JavaScript snippet might look like this:

var agent=navigator.userAgent.toLowerCase();
var is_iphone = ((agent.indexOf('iphone')!=-1);
if (is_iphone) { conditional code goes here }

Rather do it as a shell script? Me too. Here's how I'd do that as a rudimentary Linux shell script:

#!/bin/sh

if [ ! -z "$(echo $HTTP_USER_AGENT | grep iPhone)" ] ; then
echo "Location: iphone/index.html"
else
echo "Location: index2.html"
fi
exit 0

You can also have the conditional directly in CSS itself, if you're really into cryptic notations:






The basic idea in either case is that once you have detected that they're running an iPhone, you can modify what you deliver to them, either changing what's on the page, perhaps giving them a completely different version of the page, or even taking them to a different site entirely.

Hope that helps you out! Don't forget while you're here that I have quite a lot of iPhone help here too.