![]() |
GotW #88: Is it safe to const_cast a reference to a temporary?
Herb Sutter wrote a new Guru-of-the-Week column last month, GotW #88,
about binding a temporary object to a reference-to-const. Now if this temporary isn't const, is it safe to const_cast the reference? #include <string> #include <cassert> using std::string; string f() { return "abc"; } void h() { const string& s1 = f(); const string& s2 = s1; const_cast<string&>(s1) = "new value!"; // Safe? assert(s1 == "new value!"); // Okay? assert(s2 == "new value!"); // Okay? } See also: GotW #88 - A Candidate For the "Most Important const." http://herbsutter.spaces.live.com/blog/cns!2D4327CC297151BB!378.entry Kind regards, -- Niels Dekker http://www.xs4all.nl/~nd/dekkerware Scientific programmer at LKEB, Leiden University Medical Center |
Re: GotW #88: Is it safe to const_cast a reference to a temporary?
* Niels Dekker - no return address:
> Herb Sutter wrote a new Guru-of-the-Week column last month, GotW #88, > about binding a temporary object to a reference-to-const. Now if this > temporary isn't const, is it safe to const_cast the reference? > > #include <string> > #include <cassert> > using std::string; > > string f() { return "abc"; } > > void h() { > const string& s1 = f(); > const string& s2 = s1; > const_cast<string&>(s1) = "new value!"; // Safe? Not in standard C++, although it might be safe with a particular compiler. The short of it is that you're misinforming the compiler, and the almost as short of it is that the compiler is free to optimize away the call to f and e.g. substitute the value string("abc") wherever s1 and s2 are used. The bit-longer of it is that the temporary was const to begin with (the temporary bound to the reference, not the temporary returned by the function, although they might end up being the same), and you can't cast away /original/ const'ness with portable well-defined result. > assert(s1 == "new value!"); // Okay? > assert(s2 == "new value!"); // Okay? > } > > See also: > GotW #88 - A Candidate For the "Most Important const." > http://herbsutter.spaces.live.com/blog/cns!2D4327CC297151BB!378.entry Would IMHO be better if Herb posted his GotW questions here, as he did for the first few, and summarized the responses + his extra insights (AFAIK the PIMPL idiom GOTW with erronous auto_ptr still not corrected). Cheers, & hth., - Alf -- A: Because it messes up the order in which people normally read text. Q: Why is it such a bad thing? A: Top-posting. Q: What is the most annoying thing on usenet and in e-mail? |
Re: GotW #88: Is it safe to const_cast a reference to a temporary?
* Alf P. Steinbach:
> * Niels Dekker - no return address: >> Herb Sutter wrote a new Guru-of-the-Week column last month, GotW #88, >> about binding a temporary object to a reference-to-const. Now if this >> temporary isn't const, is it safe to const_cast the reference? >> >> #include <string> >> #include <cassert> >> using std::string; >> >> string f() { return "abc"; } >> void h() { >> const string& s1 = f(); >> const string& s2 = s1; >> const_cast<string&>(s1) = "new value!"; // Safe? > > Not in standard C++, although it might be safe with a particular compiler. > > The short of it is that you're misinforming the compiler, and the almost > as short of it is that the compiler is free to optimize away the call to > f and e.g. substitute the value string("abc") wherever s1 and s2 are used. Except for taking address, which includes passing by reference, which applies to just about anything it could be used for. I didn't think of that. And I'm not sure there is any qualification of "used" that would make the last statement above true. Sorry about hasty trigger finger -- correcting this before Someone Else does. However, rest applies: > The bit-longer of it is that the temporary was const to begin with (the > temporary bound to the reference, not the temporary returned by the > function, although they might end up being the same), and you can't cast > away /original/ const'ness with portable well-defined result. > > >> assert(s1 == "new value!"); // Okay? >> assert(s2 == "new value!"); // Okay? >> } >> >> See also: >> GotW #88 - A Candidate For the "Most Important const." >> http://herbsutter.spaces.live.com/blog/cns!2D4327CC297151BB!378.entry > > Would IMHO be better if Herb posted his GotW questions here, as he did > for the first few, and summarized the responses + his extra insights > (AFAIK the PIMPL idiom GOTW with erronous auto_ptr still not corrected). Cheers, & hth., - Alf (wondering why the Send button doesn't say "Do you Really want to Send?") -- A: Because it messes up the order in which people normally read text. Q: Why is it such a bad thing? A: Top-posting. Q: What is the most annoying thing on usenet and in e-mail? |
Re: GotW #88: Is it safe to const_cast a reference to a temporary?
> Herb Sutter wrote a new Guru-of-the-Week column last month, GotW #88,
> about binding a temporary object to a reference-to-const. Now if this > temporary isn't const, is it safe to const_cast the reference? > const string& s1 = f(); > const_cast<string&>(s1) = "new value!"; // Safe? Alf P. Steinbach replied: >> Not in standard C++, although it might be safe with a particular compiler. Thanks! It looked too much like a hack anyway... >> The short of it is that you're misinforming the compiler, and the almost >> as short of it is that the compiler is free to optimize away the call to >> f and e.g. substitute the value string("abc") wherever s1 and s2 are used. > > Except for taking address, which includes passing by reference, which > applies to just about anything it could be used for. Okay, that makes sense. But I guess in C++0x we can bind the temporary object returned by f() to an rvalue reference: string && s1 = f(); // C++0x s1 = "new value!"; Still this wouldn't work if the return type of f() would have been declared as "const": const string f(void); Right? Kind regards, Niels |
Re: GotW #88: Is it safe to const_cast a reference to a temporary?
On Feb 2, 10:37 pm, "Alf P. Steinbach" <al...@start.no> wrote:
> * Niels Dekker - no return address: > > Herb Sutter wrote a new Guru-of-the-Week column last month, GotW #88, > > about binding a temporary object to a reference-to-const. Now if this > > temporary isn't const, is it safe to const_cast the reference? > > #include <string> > > #include <cassert> > > using std::string; > > string f() { return "abc"; } > > void h() { > > const string& s1 = f(); > > const string& s2 = s1; > > const_cast<string&>(s1) = "new value!"; // Safe? > Not in standard C++, although it might be safe with a > particular compiler. This has always been my believe as well, and I'm almost sure that I once read somewhere in the standard something to the effect that any attempt to modify a temporary (e.g. by such tricks) was undefined behavior. The last time I looked for it, however, I couldn't find it. So could you tell me where in the standard this is forbidden. It's tricky, because you are allowed to call a non-const function on a temporary, and the assignment operator of std::string is a non-const function. There are really two separate questions: what about: int const& i = 42 ; const_cast< int& >( i ) = 0 ; I certainly hope that it's illegal, but that was the case I was actually looking for, and couldn't find. It's frustrating me, because I really want the example with int to be undefined behavior, and I'm sure that it is, but I can't find the words in the standard to back it up. > The short of it is that you're misinforming the compiler, and > the almost as short of it is that the compiler is free to > optimize away the call to f and e.g. substitute the value > string("abc") wherever s1 and s2 are used. This isn't generally true; a compiler cannot assume that because a reference to const evaluated to a certain value once, it will evaluate to that value a second time. The question is: when can it make such assumptions? > The bit-longer of it is that the temporary was const to begin > with (the temporary bound to the reference, not the temporary > returned by the function, although they might end up being the > same), and you can't cast away /original/ const'ness with > portable well-defined result. Except that in the example, the temporary wasn't const to begin with. The function f() retuns an std::string, and not an std::string const. If f() had returned an std::string const, then the code definitely has undefined behavior. Again, we have two different cases to consider. Temporaries of non-class types are never const. > > assert(s1 == "new value!"); // Okay? > > assert(s2 == "new value!"); // Okay? > > } > > See also: > > GotW #88 - A Candidate For the "Most Important const." > >http://herbsutter.spaces.live.com/blog/cns!2D4327CC297151BB!378.entry > Would IMHO be better if Herb posted his GotW questions here, > as he did for the first few, More than just a few:-). Certainly, if he wants people to comment on them, or even read them, he should post them somewhere that people read regularly. Nothing against Herb's site, but I don't visit it every week just to see if there's anything new. -- James Kanze (GABI Software) email:james.kanze@gmail.com Conseils en informatique orientée objet/ Beratung in objektorientierter Datenverarbeitung 9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34 |
Re: GotW #88: Is it safe to const_cast a reference to a temporary?
* James Kanze:
> On Feb 2, 10:37 pm, "Alf P. Steinbach" <al...@start.no> wrote: >> * Niels Dekker - no return address: > >>> Herb Sutter wrote a new Guru-of-the-Week column last month, GotW #88, >>> about binding a temporary object to a reference-to-const. Now if this >>> temporary isn't const, is it safe to const_cast the reference? > >>> #include <string> >>> #include <cassert> >>> using std::string; > >>> string f() { return "abc"; } > >>> void h() { >>> const string& s1 = f(); >>> const string& s2 = s1; >>> const_cast<string&>(s1) = "new value!"; // Safe? > >> Not in standard C++, although it might be safe with a >> particular compiler. > > This has always been my believe as well, and I'm almost sure > that I once read somewhere in the standard something to the > effect that any attempt to modify a temporary (e.g. by such > tricks) was undefined behavior. The last time I looked for it, > however, I couldn't find it. So could you tell me where in the > standard this is forbidden. Yes. With respect to the 1998 standard there are two rules involved: first, a rule saying that modifying a (we must presume "original" is meant) const object incurs Undefined Behavior, §7.1.5.1/4, and second, a rule that that the reference can be, at the implementation's discretion, a reference to an original const object (a new const temporary that is copy constructed from the initializer), §8.5.3/5. There is a problem with this, and that is that the relevant part of §8.5.3/5, at least as I interpret is, is also the culprit responsible for requiring a copy constructor when passing an rvalue of class type to T const& argument, and as I recall it has therefore been changed the C++0x draft. Checking... Yep, in the n2315 draft (I don't have the latest) it has been changed, and the reference bound directly, no implementation discretion -- and I gather that that otherwise desirable change may just open the door for code such as above, in C++0x... And hm, what about std::auto_ptr. :-( > It's tricky, because you are > allowed to call a non-const function on a temporary, and the > assignment operator of std::string is a non-const function. I don't think that's relevant. I think you perhaps were thinking of only direct binding of the reference, as in the C++0x draft, and that what you point out here would indicate that the temporary returned by the function is not really const. And it isn't, but the temporary the reference is bound to per the C++ 1998 standard, can be original const (if a copy is made it is required to be original const). > There are really two separate questions: what about: > int const& i = 42 ; > const_cast< int& >( i ) = 0 ; > I certainly hope that it's illegal, but that was the case I was > actually looking for, and couldn't find. It's the same paragraph, §8.5.3/5, but a different part a bit further down. Also here the temporary is original const. > It's frustrating me, because I really want the example with int > to be undefined behavior, and I'm sure that it is, but I can't > find the words in the standard to back it up. See above. >> The short of it is that you're misinforming the compiler, and >> the almost as short of it is that the compiler is free to >> optimize away the call to f and e.g. substitute the value >> string("abc") wherever s1 and s2 are used. > > This isn't generally true; a compiler cannot assume that because > a reference to const evaluated to a certain value once, it will > evaluate to that value a second time. The question is: when can > it make such assumptions? Note: I corrected the original posting almost immediately (see that follow-up), because taking the address of the object is one usage where the same object must be involved. Per the 1998 standard one case for use of rvalue is as above, where the compiler is free to bind the reference to a newly constructed original const copy of the initializer, because modifying that original const object is UB. >> The bit-longer of it is that the temporary was const to begin >> with (the temporary bound to the reference, not the temporary >> returned by the function, although they might end up being the >> same), and you can't cast away /original/ const'ness with >> portable well-defined result. > > Except that in the example, the temporary wasn't const to begin > with. The function f() retuns an std::string, and not an > std::string const. If f() had returned an std::string const, > then the code definitely has undefined behavior. In the current standard the constness of f()'s result doesn't matter, because it's not necessarily the object the reference is bound to. Cheers, & hth., - Alf -- A: Because it messes up the order in which people normally read text. Q: Why is it such a bad thing? A: Top-posting. Q: What is the most annoying thing on usenet and in e-mail? |
Re: GotW #88: Is it safe to const_cast a reference to a temporary?
* Niels Dekker - no return address:
> > But I guess in C++0x we can bind the temporary object returned by f() to > an rvalue reference: > > string && s1 = f(); // C++0x > s1 = "new value!"; > > Still this wouldn't work if the return type of f() would have been > declared as "const": > const string f(void); > > Right? The last about const, yes I think obviously, but the first, I don't know: if rvalue references are to be useful that would be one case they should be able to handle, but I don't know more about rvalue references than the general impression that they can perform some black magic, and essentially provide language support for "moving construction", allowing you to freely implement functions that return large objects by value. Cheers, - Alf -- A: Because it messes up the order in which people normally read text. Q: Why is it such a bad thing? A: Top-posting. Q: What is the most annoying thing on usenet and in e-mail? |
Re: GotW #88: Is it safe to const_cast a reference to a temporary?
On Feb 3, 3:55 pm, "Alf P. Steinbach" <al...@start.no> wrote:
> * James Kanze: > > On Feb 2, 10:37 pm, "Alf P. Steinbach" <al...@start.no> wrote: > >> * Niels Dekker - no return address: > >>> Herb Sutter wrote a new Guru-of-the-Week column last month, GotW #88, > >>> about binding a temporary object to a reference-to-const. Now if this > >>> temporary isn't const, is it safe to const_cast the reference? > >>> #include <string> > >>> #include <cassert> > >>> using std::string; > >>> string f() { return "abc"; } > >>> void h() { > >>> const string& s1 = f(); > >>> const string& s2 = s1; > >>> const_cast<string&>(s1) = "new value!"; // Safe? > >> Not in standard C++, although it might be safe with a > >> particular compiler. > > This has always been my believe as well, and I'm almost sure > > that I once read somewhere in the standard something to the > > effect that any attempt to modify a temporary (e.g. by such > > tricks) was undefined behavior. The last time I looked for it, > > however, I couldn't find it. So could you tell me where in the > > standard this is forbidden. > Yes. With respect to the 1998 standard there are two rules > involved: first, a rule saying that modifying a (we must > presume "original" is meant) const object incurs Undefined > Behavior, §7.1.5.1/4, and second, a rule that that the > reference can be, at the implementation's discretion, a > reference to an original const object (a new const temporary > that is copy constructed from the initializer), §8.5.3/5. I don't see where "orignal" is relevant. This rule concerns the object itself, and not the type of expression used to refer to it. In the sample code above, none of the objects are const. That is, IMHO, the crux of the problem. (And I very definitely remember reading somewhere that attempting to modify an rvalue---or maybe it was only an rvalue of non-class type---was undefined behavior, regardless of the const.) > There is a problem with this, and that is that the relevant part of > §8.5.3/5, at least as I interpret is, is also the culprit responsible > for requiring a copy constructor when passing an rvalue of class type to > T const& argument, and as I recall it has therefore been changed the > C++0x draft. Checking... > Yep, in the n2315 draft (I don't have the latest) it has been changed, > and the reference bound directly, no implementation discretion -- and > I gather that that otherwise desirable change may just open the door for > code such as above, in C++0x... And hm, what about std::auto_ptr. :-( > > It's tricky, because you are > > allowed to call a non-const function on a temporary, and the > > assignment operator of std::string is a non-const function. > I don't think that's relevant. I think you perhaps were thinking of > only direct binding of the reference, as in the C++0x draft, and that > what you point out here would indicate that the temporary returned by > the function is not really const. And it isn't, but the temporary the > reference is bound to per the C++ 1998 standard, can be original const > (if a copy is made it is required to be original const). No. I'm thinking of the object itself. The temporary, and not the reference. The const in the reference doesn't affect the const-ness of the temporary. For example: int i = 43 ; int const& ri = i ; const_cast< int& >( ri ) = 0 ; is perfectly defined and legal. (Which, of course, doesn't say that it's good code.) In the original code, the function returns a non-const object. Something like: f() = "whatever" ; is perfectly legal (although not very useful). So is: std::string s( "abc" ) ; std::string const& rs = s ; static_cast< std::string& >( rs ) = "whatever" ; The original code looks very much like a combination of these two cases, and in the absense of some special rule, is clearly legal and well defined. > > There are really two separate questions: what about: > > int const& i = 42 ; > > const_cast< int& >( i ) = 0 ; > > I certainly hope that it's illegal, but that was the case I was > > actually looking for, and couldn't find. > It's the same paragraph, §8.5.3/5, but a different part a bit > further down. Also here the temporary is original const. I don't think so. In general, "temporary" and "rvalue" are synonyms. And §3.10/9 very clearly says "Class rvalues can have cv-qualified types; non-class rvalues always have cv-unqualified types." A temporary of type int can never be const. > > It's frustrating me, because I really want the example with > > int to be undefined behavior, and I'm sure that it is, but I > > can't find the words in the standard to back it up. > See above. I get the feeling that there must be something obvious I'm missing, but I just can't see it. The reference must refer to const, but references to const can definitly be bound to non-const objects, and when they are, casting away const and modifying the object is well defined behavior. So we need a special rule to forbid modifying the (non-const) temporary. > >> The short of it is that you're misinforming the compiler, > >> and the almost as short of it is that the compiler is free > >> to optimize away the call to f and e.g. substitute the > >> value string("abc") wherever s1 and s2 are used. > > This isn't generally true; a compiler cannot assume that > > because a reference to const evaluated to a certain value > > once, it will evaluate to that value a second time. The > > question is: when can it make such assumptions? > Note: I corrected the original posting almost immediately (see > that follow-up), because taking the address of the object is > one usage where the same object must be involved. That wasn't what I was thinking of. The most obvious example: int global = 0 ; void f( int const& i ) { std::cout << i << std::endl ; ++ global ; std::cout << i << std::endl ; } int main() { f( global ) ; } This program had better output 0, then 1. Change f() to: void f( int const& i ) { std::cout << i << std::endl ; ++ const_cast< int& >( i ) ; std::cout << i << std::endl ; } and it's still well defined. Declare "global" const, and the above involves undefined behavior, however. The defined-ness depends on the const-ness of the object refered to, and not on the const-ness of the reference. Which brings us back to my original problem: the temporaries here don't have a const type---and in the case of non-class types, cannot have a const types. So there must be some additional rule somewhere which makes attempting to modify them undefined behavior. > Per the 1998 standard one case for use of rvalue is as above, > where the compiler is free to bind the reference to a newly > constructed original const copy of the initializer, because > modifying that original const object is UB. > >> The bit-longer of it is that the temporary was const to begin > >> with (the temporary bound to the reference, not the temporary > >> returned by the function, although they might end up being the > >> same), and you can't cast away /original/ const'ness with > >> portable well-defined result. > > Except that in the example, the temporary wasn't const to begin > > with. The function f() retuns an std::string, and not an > > std::string const. If f() had returned an std::string const, > > then the code definitely has undefined behavior. > In the current standard the constness of f()'s result doesn't > matter, because it's not necessarily the object the reference > is bound to. Which has the consequence that even if f() were declared to return a std::string const, the code might have defined behavior? (In fact, the standards committee has changed this, and I think we can ignore it. The problem that I see is that the original temporary object is not const. And so casting away const and modifying it is well defined behavior.) -- James Kanze (GABI Software) email:james.kanze@gmail.com Conseils en informatique orientée objet/ Beratung in objektorientierter Datenverarbeitung 9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34 |
Re: GotW #88: Is it safe to const_cast a reference to a temporary?
* James Kanze:
> On Feb 3, 3:55 pm, "Alf P. Steinbach" <al...@start.no> wrote: >> * James Kanze: >>> On Feb 2, 10:37 pm, "Alf P. Steinbach" <al...@start.no> wrote: >>>> * Niels Dekker - no return address: > >>>>> Herb Sutter wrote a new Guru-of-the-Week column last month, GotW #88, >>>>> about binding a temporary object to a reference-to-const. Now if this >>>>> temporary isn't const, is it safe to const_cast the reference? > >>>>> #include <string> >>>>> #include <cassert> >>>>> using std::string; > >>>>> string f() { return "abc"; } > >>>>> void h() { >>>>> const string& s1 = f(); >>>>> const string& s2 = s1; >>>>> const_cast<string&>(s1) = "new value!"; // Safe? > >>>> Not in standard C++, although it might be safe with a >>>> particular compiler. > >>> This has always been my believe as well, and I'm almost sure >>> that I once read somewhere in the standard something to the >>> effect that any attempt to modify a temporary (e.g. by such >>> tricks) was undefined behavior. The last time I looked for it, >>> however, I couldn't find it. So could you tell me where in the >>> standard this is forbidden. > >> Yes. With respect to the 1998 standard there are two rules >> involved: first, a rule saying that modifying a (we must >> presume "original" is meant) const object incurs Undefined >> Behavior, §7.1.5.1/4, and second, a rule that that the >> reference can be, at the implementation's discretion, a >> reference to an original const object (a new const temporary >> that is copy constructed from the initializer), §8.5.3/5. > > I don't see where "orignal" is relevant. This rule concerns the > object itself, and not the type of expression used to refer to > it. In the sample code above, none of the objects are const. > That is, IMHO, the crux of the problem. No, the reference's type -- its cv-qualification -- is very relevant because the cv-qualification can be transferred to a temporary created for the purpose, and no, it's not the case that one is guaranteed that none of the objects are const, in the code above. Trying to be utterly unambigiously clear: §5.1.3/5 "A reference to type "cv1 T1" is initialized by an expression of type "cv2 T2" as follows: ... - Otherwise, the reference shall be to a non-volatile const type (i.e. cv1 shall be const) [Example: ...] - If the initializer expression is an rvalue, with T2 a class type, and "cv1 T1" is reference-compatible with "cv2 T2", the reference is bound in one of the following ways (the choice is implementation-defined): - The reference is bound to the object represented by the rvalue (see 3.10) or to a sub-object within that object. - A temporary of type "cv1 T2" [sic] is created, and a constructor is called to copy the entire rvalue into the temporary. The reference is bound to the temporary or to sub-object within the temporary. The last alternative above is the one that, together with UB for modification of const, dictates UB for the example code. And what happens in this case is that "cv1" is "const", from the declaration of the /reference/ s1, "T2" is "string", from the declaration of f(), and the type of the temporary created is then "const string" -- which is not a typo, but intentional ([sic] means essentially that "this is /not/ a typo"). This is then what the reference is bound to when this case is chosen, namely a new temporary of type "const string". And the pure possibility of that alternative being chosen, and that alternative implying UB, means that formally the code is UB. > (And I very definitely > remember reading somewhere that attempting to modify an > rvalue---or maybe it was only an rvalue of non-class type---was > undefined behavior, regardless of the const.) For rvalue of non-class type yes I think so, but for rvalue of class type, no, quite the opposite. But I've done enough standardeeze for today, I think. ;-) >> There is a problem with this, and that is that the relevant part of >> §8.5.3/5, at least as I interpret is, is also the culprit responsible >> for requiring a copy constructor when passing an rvalue of class type to >> T const& argument, and as I recall it has therefore been changed the >> C++0x draft. Checking... > >> Yep, in the n2315 draft (I don't have the latest) it has been changed, >> and the reference bound directly, no implementation discretion -- and >> I gather that that otherwise desirable change may just open the door for >> code such as above, in C++0x... And hm, what about std::auto_ptr. :-( > >>> It's tricky, because you are >>> allowed to call a non-const function on a temporary, and the >>> assignment operator of std::string is a non-const function. > >> I don't think that's relevant. I think you perhaps were thinking of >> only direct binding of the reference, as in the C++0x draft, and that >> what you point out here would indicate that the temporary returned by >> the function is not really const. And it isn't, but the temporary the >> reference is bound to per the C++ 1998 standard, can be original const >> (if a copy is made it is required to be original const). > > No. I'm thinking of the object itself. The temporary, and not > the reference. The const in the reference doesn't affect the > const-ness of the temporary. Does, in this situation -- see above. Cheers, & hth., - Alf -- A: Because it messes up the order in which people normally read text. Q: Why is it such a bad thing? A: Top-posting. Q: What is the most annoying thing on usenet and in e-mail? |
Re: GotW #88: Is it safe to const_cast a reference to a temporary?
* Alf P. Steinbach:
> > Trying to be utterly unambigiously clear: > > §5.1.3/5 Huh, who ordered /that/? Of course it should be §8.5.3/5, as I mentioned several times already. There's a gremlin in my keyboard. Cheers, - Alf -- A: Because it messes up the order in which people normally read text. Q: Why is it such a bad thing? A: Top-posting. Q: What is the most annoying thing on usenet and in e-mail? |
| All times are GMT. The time now is 09:26 PM. |
Powered by vBulletin®. Copyright ©2000 - 2013, vBulletin Solutions, Inc.
SEO by vBSEO ©2010, Crawlability, Inc.