0

This is a follow-up question from: constructing string from NULL?

The following:

void test(const std::string& s);

int main(){
  test(NULL);
}

Fails when run, but is legal c++.

In order to try to catch some of those cases, as an alternative ive considering if std::string can be replaced in the following way:

#include <string>


namespace {
namespace std {
    struct string : public ::std::string { //so far everything is good

    };
}
}


int main ()
 {
   std::string hello;//failure: ambiguous symbol

   return 0;
 }

Gives the following error:

<source>(17): error C2872: 'std': ambiguous symbol

C:/data/msvc/14.22.27905/include\string(19): note: could be 'std'

<source>(7): note: or       '`anonymous-namespace'::std'

<source>(17): error C2872: 'std': ambiguous symbol

C:/data/msvc/14.22.27905/include\string(19): note: could be 'std'

<source>(7): note: or       '`anonymous-namespace'::std'

Compiler returned: 2

I guess it is not possible to make it resolve without writing a more (fully) qualified name ? but is it possible to write std::string in the global namespace and have it resolve to something else while ::std::string is a valid type.

Background: after some failed attempts with cppcheck and cpp core check im trying to find all cases of std::string str = 0 or NULL or nullptr - since those will fail at runtime. And I thought this might be a way forward.

I ended up modifying the basic_string template ala. basic_string(int) = delete; basic_string(::std::nullptr_t) = delete; - this won't catch all cases but does indeed seem to catch the direct cases at least

12
  • I'd start with asking - why would you want to resolve std::string lookup to something else? Commented Oct 11, 2019 at 9:47
  • @bartop look at last part of question, background Commented Oct 11, 2019 at 9:48
  • 3
    Why would you want to "catch" that? What does that even mean? Why is std::string str = 0 in your code in the first place? Why don't you fix these code errors instead of trying to write a custom type that tries to hide the errors? Sorry for the bunch of questions, but it seems you have a XY problem, and it would probably be better to explain in detail your situation and your goal. Don't show us your (attempt of a) solution, show us your problem. Commented Oct 11, 2019 at 9:54
  • 1
    Some additional thoughts: If you are scared that a regex won't find all cases of this problem in your code - how can you be sure you'll be able to inject this custom type in every place of the code where it'd needed to be? If you want to replace std::string with a custom type that silently ignores nullptr assignments, why don't you simply replace all occurences of std::string in your code base with MyCustomNullPtrSafeString instead of a custom type named std::string? Commented Oct 11, 2019 at 10:03
  • 2
    Also your question should be self-contained. If it's helpful to read other questions you posted, or other external information to fully understand the context of your situation, you might want to link to that external information and summarize it in your question. Commented Oct 11, 2019 at 10:06

2 Answers 2

1

Resolve std::string to something else than ::std::string - is it possible?
[...]
...as an alternative ive considering if std::string can be replaced in the following way...

As far as I know, it is not possible outside of your anonymous namespace scope because you have no way to resolve the ambiguity (not to my knowledge).

As you can see below, since you are inside the scope of the anonymous namespace, it will be fine:

#include <string>

namespace
{
    namespace std
    {
        struct string : public ::std::string
        {

        };
    }
    std::string hello; // Fine
}


int main()
{
    std::string hello2; // Cannot be something else that ambiguous

    return 0;
}

But even worse, the problem is not about std::string itself but about the std namespace.

Indeed, outside of the scope of your anonymous namespace, every call of std becomes ambiguous too.
This is the error pointed by the compiler. There are two std namespaces accessible as is from the global scope.

So the following example becomes broken:

#include <string>
#include <vector>

namespace
{
    namespace std
    {
        struct string : public ::std::string
        {

        };
    }
    std::string hello; // Fine
}


int main()
{
    std::vector<int> a; // FAIL: reference to 'std' is ambiguous

    return 0;
}

To fix this ambiguity over accessing the original std namespace, you'll need to write it as follows:

::std::vector<int> a; // Fully qualified name: Only way to refer to the `::std` namespace

As you can see, it still does not solve the issue and even worse, it adds a huge inconvenience.


Therefore the morality is:

  • Do not hide an already existing type but create a distinct one instead.
  • In the same way, do not hide a namespace (by defining the same namespace into an anonymous one --> evil).

(I'm open to any improvement proposal of this answer)

Sign up to request clarification or add additional context in comments.

9 Comments

You could import all the content of ::std into your own std namespace though, so that ambiguity is not insurmountable, just very inconvenient.
@MSalters But we will still encounter the first case error (where string was imported into he's own std namespace), isn't it ? Tell me if I've misunderstood something of your comment.
None of your examples actually import ::std::string; instead they define another string that's inherited from ::std::string. I.e. it's not namespace { namespace std { using string = ::std::string; }}. So I think you misunderstood my comment, yes.
@MSalters Ah ok, I now understand what you mean. But it does still not compile as is. Moreover, the OP want to redefine the std::string.
I'm pretty sure that darune isn't trying to redefine std::string. I suspect you're not entirely familiar with the wording used in the C++ Standard? E.g. the "One Defintion Rule"? AFAICT, the goal is to affect the name lookup of std::string. Since this involves two names, there are two name lookups.
|
0

Could you use this kind of solution : Rather than working on std::string, you work on the function that will cause the problem

#include <string>

void test(const std::string& s){

}
// add a new function which use a pointer : 
void test (const char* _Nonnull s) {
    test(std::string(s));

}
int main()
{
    test(NULL);
    return 0;
}

Then clang will generate a warning : 1 warning generated.

ASM generation compiler returned: 0

<source>:13:14: warning: null passed to a callee that requires a non-null argument [-Wnonnull]

test(NULL);

see https://godbolt.org/z/PujFor

6 Comments

I ended up modifying the basic_string template ala. ` basic_string(int) = delete; basic_string(::std::nullptr_t) = delete;` - this won't catch all cases but does indeed seem to catch the direct cases at least
_Nonnull isn't standard c++ afaik ?
@darune not it is clang specific : for gcc it is __attribute__((nonnull)) for Microsoft it is __assume. The standards c++11 and higher have the static_assert which perform compile time assertion checking.
@darune you can add your solution as answer to this question.
@darune indeed, It seems that the microsoft compiler does not provide the same level of checking that gcc and clang (__assume does no check as I first though). According to this question : stackoverflow.com/questions/38641514/… the MSVC only uses assume as a hint. I did not find any way to do compile time checking : all I found was runtime check, which is not what you want
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.