Unqualified lookup fails...

W

Werner

Hi All,

The following code fails to compile on GCC 4.7.
I've now learned because of its compliance. I've
also noticing it failing under Comeau. On moving
the functions in the anonymous namespace above the
definition of Xx, it compiles:

------------------------------------------------
template <class T>
struct Xx
{
Xx()
{
T inst;
int n = sequenceSize( inst );
}
};


struct Header1
{
int sequenceSize_;
};

struct Header2
{
int itemCount_;
};

namespace {
int sequenceSize( const Header1& h )
{
return h.sequenceSize_;
}
int sequenceSize( const Header2& h )
{
return h.itemCount_;
}
}


void testUnqualifiedLookup()
{
Xx<Header1> xH1;
Xx<Header2> xH2;
}

Now (the question):

What would be the best way to fix this kind of problem?

A couple of options exist:

1) Functions sequenceSize would be found if they were
in the same namespaces as classes "Headerx" (by ADL).

2) A declaration of sequenceSize need at least exist
prior to the template definition.

I now have a case like this:

struct MsgX
{
int sequenceSizeOfA_;
int sequenceSizeOfB_;

std::vector<A> a_;
std::vector<B> b_;
};

I still would like the calling code (in the template
to work, therefore:

//Translation Unit A:
namespace {
int sequenceSize( const MsgX& msg )
{
return msg.sequenceSizeOfA_;
}
}
#include "Xx.h"

//Translation Unit B:
namespace {
int sequenceSize( const MsgX& msg )
{
return msg.sequenceSizeOfB_;
}
}
#include "Xx.h"

.... but this makes the compilation dependent on
the order of inclusion. Also, in this case
I cannot use ADL, because in both instances
of sequenceSz, the argument type is the same.

Now the question:

1) Is it bad of Xx to depend on an unqualified name?

2) If I'm not able to change Xx, what would be a good
solution - to include the declaration of sequenceSize
above Xx?

3) If I am allowed to change Xx, what would you do?

Kind regards,

Werner
 
S

SG

------------------------------------------------
template <class T>
struct Xx
{
  Xx()
  {
    T inst;
    int n = sequenceSize( inst );
  }

};
[snip]

A couple of options exist:

1) Functions sequenceSize would be found if they were
in the same namespaces as classes "Headerx" (by ADL).

This sounds like the way to go.
2) A declaration of sequenceSize need at least exist
prior to the template definition.

No, don't do that. Rely on ADL.
I now have a case like this:

struct MsgX
{
  int sequenceSizeOfA_;
  int sequenceSizeOfB_;
  std::vector<A> a_;
  std::vector<B> b_;
};

I still would like the calling code (in the template
to work, therefore:

//Translation Unit A:
namespace {
  int sequenceSize( const MsgX& msg )
  {
    return msg.sequenceSizeOfA_;
  }}

#include "Xx.h"

//Translation Unit B:
namespace {
  int sequenceSize( const MsgX& msg )
  {
    return msg.sequenceSizeOfB_;
  }}

#include "Xx.h"

... but this makes the compilation dependent on
the order of inclusion.

Not only that. I believe it is also a violation of the ODR (one
definition rule) since you rely on two different versions of
Also, in this case
I cannot use ADL, because in both instances
of sequenceSz, the argument type is the same.

You could use a wrapper type + ADL:

struct wrapper_MsgX_A
{
MsX data;
};

int sequenceSize(wrapper_MsgX_A const& x)
{ return x.data.sequenceSizeOfA_; }

...

Xx said:
Now the question:

1) Is it bad of Xx to depend on an unqualified name?

No. It's good since it allows ADL and ADL is a good thing.
2) If I'm not able to change Xx, what would be a good
solution - to include the declaration of sequenceSize
above Xx?
No.

3) If I am allowed to change Xx, what would you do?

Keep it as it is or introduce another layer of indirection via a
traits
class or additional template parameter for Xx.


Cheers!
SG
 
V

Victor Bazarov

The following code fails to compile on GCC 4.7.
I've now learned because of its compliance. I've
also noticing it failing under Comeau. On moving
the functions in the anonymous namespace above the
definition of Xx, it compiles:

------------------------------------------------
template <class T>
struct Xx
{
Xx()
{
T inst;
int n = sequenceSize( inst );
}
};


struct Header1
{
int sequenceSize_;
};

struct Header2
{
int itemCount_;
};

namespace {
int sequenceSize( const Header1& h )
{
return h.sequenceSize_;
}
int sequenceSize( const Header2& h )
{
return h.itemCount_;
}
}


void testUnqualifiedLookup()
{
Xx<Header1> xH1;
Xx<Header2> xH2;
}

So, the function 'sequenceSize' used in the template has to have been
declared at the point of its first encounter by the compiler. Can you
declare it somehow without defining? See below for different examples.
Now (the question):

What would be the best way to fix this kind of problem?

"The best" depends on the definition of "good" and the criteria for
comparing two "good" solutions.
A couple of options exist:

1) Functions sequenceSize would be found if they were
in the same namespaces as classes "Headerx" (by ADL).

Which is a very good idea, BTW. Those need to be members, which would
make them class-specific or instance-specific, which is essentially what
they are now anyway. Of course that would make the classes or instances
"sequence-aware", IOW there will be some design dependency between the
class and the concept of "sequence" or "sequence size". Is that bad?
2) A declaration of sequenceSize need at least exist
prior to the template definition.

Yes, that's the first thing that came to my mind.
I now have a case like this:

struct MsgX
{
int sequenceSizeOfA_;
int sequenceSizeOfB_;

std::vector<A> a_;
std::vector<B> b_;
};

I still would like the calling code (in the template
to work, therefore:

//Translation Unit A:
namespace {
int sequenceSize( const MsgX& msg )
{
return msg.sequenceSizeOfA_;
}
}
#include "Xx.h"

//Translation Unit B:
namespace {
int sequenceSize( const MsgX& msg )
{
return msg.sequenceSizeOfB_;
}
}
#include "Xx.h"

... but this makes the compilation dependent on
the order of inclusion. Also, in this case
I cannot use ADL, because in both instances
of sequenceSz, the argument type is the same.

Now the question:

1) Is it bad of Xx to depend on an unqualified name?

This is too general a question to be answered with certainty, so the
answer is "it depends". And there are too many factors to list, IMO.
2) If I'm not able to change Xx, what would be a good
solution - to include the declaration of sequenceSize
above Xx?

You can always wrap the header in which Xx comes in your own header and
include anything before and/or after that. Depending on your build
environment there are ways to hide the original header from being
included "by mistake".
3) If I am allowed to change Xx, what would you do?

I'd tell you to go for it <g>... On a serious note, I don't know.
Again, it depends. You need to look at the overall design of your
system and see whether it makes sense in your case to make 'Header1' and
'Header2' classes to have an inlined member function 'int
sequenceSize()' that would return the respective values.

Here is another solution, and if you can't change the header in which Xx
template resides, wrap it to add the declaration:

// -- inside the wrapper header
namespace {
template<class T> int sequenceSize(const T&);
}
// -- the wrapping header includes the Xx header:
template <class T>
struct Xx
{
int m_size;
Xx() : m_size(sequenceSize(T()))
{
}
};
// ---- end of the wrapper

struct Header1
{
int sequenceSize_;
};

struct Header2
{
int itemCount_;
};

namespace {
template<>
int sequenceSize<Header1>( const Header1& h )
{
return h.sequenceSize_;
}
template<>
int sequenceSize<Header2>( const Header2& h )
{
return h.itemCount_;
}
}

int main()
{
Xx<Header1> xH1;
Xx<Header2> xH2;
}

Keep in mind that if you don't need your 'sequenceSize' specializations
in the unnamed namespace, remove the declaration of the template from it
as well. That's what I'd do anyway.

V
 
W

Werner

Not only that. I believe it is also a violation of the ODR (one
definition rule) since you rely on two different versions of
Xx&lt;MessageX&gt;::Xx. So, this is not even an option unless you are fine
with unedfined behaviour.

Hmmm. I did not even think of that possibility. This would then
probably be why they decided to have that rule in the first place
(declaration visible at point of definition, as opposed to point
of instantiation). I've discovered this when moving to GCC 4.7.
Keep it as it is or introduce another layer of indirection via a
traits
class or additional template parameter for Xx.

Yes, in fact Xx has an additional template parameter defining the
data, but is was not used in sequenceSize.

I've now resolved to this:

template <class HdrT, class DataT>
struct HdrSequenceMsgModel
{
HdrSequenceMsgModel()
{
HdrT inst;
std::size_t n = sequenceSize( inst, boost::mpl::identity<DataT>() );
}
};

and...

namespace SomeSpace{
class Header;
class Data;
std::size_t sequenceSize( const Header&, boost::mpl::identity<Data> );
};

Thank you,

Regards,

W
 
W

Werner

[snip]
Here is another solution, and if you can't change the header in which Xx
template resides, wrap it to add the declaration:

    // -- inside the wrapper header
    namespace {
       template<class T> int sequenceSize(const T&);
    }
    // -- the wrapping header includes the Xx header:
    template <class T>
    struct Xx
    {
       int m_size;
       Xx() : m_size(sequenceSize(T()))
       {
       }
    };
    // ---- end of the wrapper

    struct Header1
    {
       int sequenceSize_;
    };

    struct Header2
    {
       int itemCount_;
    };

    namespace {
       template<>
       int sequenceSize<Header1>( const Header1& h )
       {
          return h.sequenceSize_;
       }
       template<>
       int sequenceSize<Header2>( const Header2& h )
       {
          return h.itemCount_;
       }
    }

    int main()
    {
       Xx<Header1> xH1;
       Xx<Header2> xH2;
    }

Keep in mind that if you don't need your 'sequenceSize' specializations
in the unnamed namespace, remove the declaration of the template from it
as well.  That's what I'd do anyway.

Interesting idea. I haven't thought of using a wrapper header. It
would still not work for the last case documented, where one
has one type containing two lists:

struct MsgX
{
int sequenceSizeOfA_;
int sequenceSizeOfB_;

std::vector<A> a_;
std::vector<B> b_;
};

....but it is certainly a nice solution for similar problems
if one is not able to modify the template.

Thanks,

Regards, Werner
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,764
Messages
2,569,564
Members
45,040
Latest member
papereejit

Latest Threads

Top