Jump to content
  • Advertisement
irreversible

Function specialization resolution in namespaces

Recommended Posts

I'm guessing the problem might be tied to my overall design, but I'd like to know why this behavior happens. Some background:

I have a logger class, which implements a number of << operators for common types. 

namespace logging {
	class log {
		public:
...
			_FRIEND _INLINE
			logging::log& operator << (IN logging::log& l, IN void* v) {
				LOG_APPEND_SPRINTF_MSG(32, "%p", v); return l;
			}
...
		};
}
		

There's a reason I singled out the overload for void*. My vector class provides an automatic cast operator to float*. In order to output a vector, however, I provide a special case for the << operator in global namespace:

_INLINE
logging::log& operator<<(logging::log& log, const math::vec3& v)
{
    log << '(';
    log.append_float(v.x);
    log << ", ";
    log.append_float(v.y);
    log << ", ";
    log.append_float(v.z);
    log << ')';

    logging::dispatch_log_message(log);

    return log;
}

Now, if I log a vec3 in a function that lies in the global namespace, the proper operator for << gets called. However, when logging from a function that resides inside any namespace, Visual Studio first casts the vector to float* and then forwards that to the void* operator.

For now I only know one bad solution apart from rewriting my logging class - define the << operator for vec3 in the respective namespace (this has to be done for all namespaces that want to log a vector type).

namespace mynamespace {
	logging::log& operator<<(logging::log& log, const math::vec3& v) ...
}

void mynamespace:foo(math::vec3& v){
	log << v << endl;
}

This extremely annoying. What prompts this behavior/operator resolution? More importantly, can this be solved without expelling the base type operators from the logging class (I mean, frankly I haven't even tried, but for now I'm also using certain internal functions so I would need to restructure the entire logging class to boot; besides - I'd like to know if there's a simpler solution out of sheer principle :) )?

Share this post


Link to post
Share on other sites
Advertisement

I'm not sure how it even finds the void * version.  Is there a "using namespace logging" somewhere?

I can't stand namespaces myself.  Never use them unless I'm forced to. Somehow they seem to cause more problems than they fix.
 

Share this post


Link to post
Share on other sites
4 hours ago, Gnollrunner said:

I'm not sure how it even finds the void * version.  Is there a "using namespace logging" somewhere?

I can't stand namespaces myself.  Never use them unless I'm forced to. Somehow they seem to cause more problems than they fix.
 

The void* operator is a friend member of the logging class - it doesn't need exposure to the global namespace.

I personally love structuring my code with namespaces. It's a way to filter everything.

Share this post


Link to post
Share on other sites
1 hour ago, irreversible said:

The void* operator is a friend member of the logging class - it doesn't need exposure to the global namespace.

Oh I get it _FRIEND is friend and that makes it a non-member.  Well I have no clue. I usually start changing things until I zero in on the problem. C++ has gotten so complex it could very well be a compiler bug. I just update my visual studio and got a raft or error messages I didn't get earlier in the day.

Share this post


Link to post
Share on other sites

Doesn't your compiler give you a warning?

Your code snipets are not enough to be able to see what is happening.

 

Share this post


Link to post
Share on other sites

Nope - no warning or anything. The wrong operator just is called blindly. As for it being a compiler bug - it's possible, although it would be nice if I could get some external verification for this since I only have VS set up on my rig. I can't recall if this used to be the case in VS2013 as well or not...

As for the code - there isn't much else to show really. This is kinda the minimal example. I might write up a compileable test program when I get back to my computer.

Share this post


Link to post
Share on other sites

I highly doubt it being the compiler's fault but if you do a test program I would gladly take a look at it.

Edited by Jon Diego

Share this post


Link to post
Share on other sites

Just put your overload into the logging namespace.

As a general rule, you should never put operator overloads in the global namespace.  Always but operator overloads in the same namespace as (one of) the type(s) on which they operate.  In this case, that's class logging::log in the logging namespace.

Share this post


Link to post
Share on other sites
1 hour ago, a light breeze said:

Just put your overload into the logging namespace.

As a general rule, you should never put operator overloads in the global namespace.  Always but operator overloads in the same namespace as (one of) the type(s) on which they operate.  In this case, that's class logging::log in the logging namespace.

Hey - this works!

Like I mentioned in my original post, though - I'd like to understand why this is the case. It would seem the operator defined in the same (in this case global) namespace should, regardless of placement, be preferred by a call from the same namespace.

PS - I'm also not quite sure why not to put these specific overloads in global since they get exposed via an early using namespace logging; anyway.

Share this post


Link to post
Share on other sites

Short version: functions found through argument-dependent lookup usually have priority over names found through other methods, so the friend operator is shadowing (i.e. hiding) the operator in the global namespace.  Putting the overload in the logging namespace allows it to be found with argument-dependent lookup, which prevents it from being shadowed.  See also: unqualified lookup rules.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!