• Advertisement

C++ Char pointer/array type ambiguity

Recommended Posts

So I'm trying to design a function that acts differently, based on whether it is passed a const char/wchar_t array, or a const char/wchar_t*:

template<typename Char, size_t Length>
size_t stringLength(const Char(&pString)[Length])
{
  return Length - 1;
}

template<typename Char>
size_t stringLength(const Char* pType)
{
  return strlen(pType);
}

const char* pTest = "Test";
stringLength(pTest); // => should return 4
stringLength("Test"); // => should return 4 as well

The problem is that the last line doesn't compile, saying that the function-call is ambigous between both overloads, even though it correctly identifies the argument as "const char [8]", which works as intended if I remove the "const Char* pType" overload.

Now, why is this ambigous? As far as I understand it, the upper function should be a closer match to the argument list and thus be selected. Is there anything I have to/can do to make that work? (I'm on MSVC 2017)

Share this post


Link to post
Share on other sites
Advertisement

Correct me if I'm wrong, but it's ambiguous because char* and char[] are the same type.  Both of them are just pointers to the first character in an array of characters.

Share this post


Link to post
Share on other sites
5 minutes ago, trjh2k2 said:

Correct me if I'm wrong, but it's ambiguous because char* and char[] are the same type.  Both of them are just pointers to the first character in an array of characters.

Well, from what I understand, a size-qualified "char[X]"-array isn't exactly the same type as a char*.

For example, you can convert the char[X] to a char*, but not the other way around:

char array[4] = {};
char* pointer;

pointer = array; // works
array = pointer; // doesn't

Also the first function can't be called with char*, and will have the correct array-size if called with a char[X]. So all of this ad least made me belive that they are different types; though obviously the compiler assumes they are ambigous, maybe for the reason you wrote.

I might have another idea that I'm going to try out though, just remembered that there was a std-trait to find out if a type is an array & to get the arrays extent... though thats going to result in more messy template code, so if someone found an easier solution I'd still appreciate it :)

 

Share this post


Link to post
Share on other sites

The problem I think is not that you can't pass char[x] to a char*, but that you CAN pass "text" to both.

 

What are you trying to accomplish?  This doesn't look like a good way to check the length of a string.

Share this post


Link to post
Share on other sites
24 minutes ago, trjh2k2 said:

The problem I think is not that you can't pass char[x] to a char*, but that you CAN pass "text" to both.

Yes, this is true, yet from how I can see it this only happens via cast (char[x] => char*), so under normal overload resolution rules, I still don't see how it would be any different to:
 

void func(int x)
{
}

void func(float x)
{
}

func(0); // calls "func(int x)"

I mean you're obviously right about what happens, it just feels wrong to me :>

24 minutes ago, trjh2k2 said:

What are you trying to accomplish?  This doesn't look like a good way to check the length of a string.

Its actually being used as an optimization string-length generation as part of my custom StringView-class:

template<size_t Length>
constexpr BaseStringView(StaticString<Length> pString) : // const Type(&)[Length]
	BaseStringView(pString, StringLength<Length>(pString))
{
};

template<size_t Length>
constexpr BaseStringView(DynamicString<Length> pString) : // Type(&)[Length] => prevents issues with user-handled char-buffers
	BaseStringView(pString, StringLength(pString))
{
};

I know its technically not 100% safe, but I made sure that it doesn't break anything for me; and since I'm using a string-view I'm already in not-safe territory. As you can see I've got a second overload that gets called when I'm passing in an actual "char array[X];" that is filled from ie. an windows-API method. The actual reason why I'd need the "const char*" overload is that right now this would instead call the "const std::string&" overload, thus creating an unncessary copy & a dangling pointer (if the view is actually locally stored).
Not that it happens that often, most of my codebase has now been ported to use StringView & size-qualified strings, but there's always some places where this could still happen.

Share this post


Link to post
Share on other sites

As you said before, char[] and char* are just the same, but depending on what you want to do/need you have to cast. And yeah, you can cast from char[] to char* this way:

char array[4] = {'a','b','c','d'};
char* charPointer = nullptr;

charPointer = &array[0];

You have to point your pointer to the beginning of your char array, there is no direct assignment. You might overload operators if you really use that much char-pointer assignment.

Edited by NajeNDa

Share this post


Link to post
Share on other sites

The reference trick in the StackOverflow site is the one I've seen several times over the years:

template<typename T> void f(T* const& c){ std::cout << "pointer\n"; }
template<typename T, size_t N> void f(T(&)[N]){ std::cout << "array\n"; }

Even though you as a programmer don't know, your compiler hitting the code can potentially know.  It works if the parameter being directly passed in is known to be a fixed-length array. If it goes through a single indirection to a pointer and the indirection isn't optimized a way, then the information is lost and the compiler will deduce it as a pointer. If it goes through an indirection and the indirection gets optimized away it can still deduce it correctly.  

So even though that can work in some cases, it won't work in all cases after indirections.

 

The useful cases are almost non-existent.

 

As for the original problem here on the thread where you're trying to avoid taking the string length, that's not much of a benefit.  You're trying to simplify the interface, but instead you are adding complexity by having an additional entry.  Instead of having only one interface:  (buffer, size), you've now got two interfaces: (buffer, size) and (fixed-length-array).  If the writer knows they've got a fixed array they can use (buffer, sizeof(buffer)). If the writer knows they've got a more traditional buffer they can use (buffer, buflen). They know the single interface is there and they need to use it.

Imagine if the C language used that in their interfaces.  You'd have the current set of twenty-ish memory functions like memmov, memcpy, memcmp, and a duplicate version of all the functions for fixed-length arrays.

 

Share this post


Link to post
Share on other sites

I guess what I meant to say is that rolling your own ways to check string length like this reads to me like a code smell / design smell kind of scenario.  If you created the array, you already know the size, so you can pass it around if you need it.

Share this post


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

 

41 minutes ago, frob said:

The reference trick in the StackOverflow site is the one I've seen several times over the years:


template<typename T> void f(T* const& c){ std::cout << "pointer\n"; }
template<typename T, size_t N> void f(T(&)[N]){ std::cout << "array\n"; }

Ah, yeah, thats what I've been looking for!

41 minutes ago, frob said:

The useful cases are almost non-existent.

 

24 minutes ago, trjh2k2 said:

I guess what I meant to say is that rolling your own ways to check string length like this reads to me like a code smell / design smell kind of scenario.  If you created the array, you already know the size, so you can pass it around if you need it.

Well, I should have been a bit more specific about my use-case: As I've mentioned I'm using my own StringView class, akin to std::experimental::basic_string_view.

Now that means that functions may have a signature as such:

bool Node::HasNode(sys::StringView strName) const
{
  return m_mNodes.count(strName) != 0;
}

where it would have been eigther "const std::string&" (for me), or possible "const char*" / "const char*, size_t" before. This has many benefits, as such std::string_view has been proposed, but thats not the point of this post. Now in my code, I might use those functions as such:
 

const auto strName = node.Attribute("name")->GetValue();
widget.SetName(strName.ToString());

const auto isVariable = node.HasNode("IsVariable");
widget.SetIsVariable(isVariable);
	
const auto visibilty = core::VariableLoader::FromAttribute<Visibility>(node, "Visibility");
widget.SetVisibility(visibilty);

const auto isEnabled = !node.HasNode("Disabled");
widget.SetEnabled(isEnabled);

Not the every function above takes a sys::StringView. And thats pretty much where I applied my optimization. std::string_view would take a const char*, and call strlen. My StringView-constructor can take a static char-array, and directly deduce the size from this - thats the reason why I don't wanna do it by hand even though I technically "know" the strings size, its simple convenience so that I can call all those functions with string literals, but without having to take a copy or determine the size.

41 minutes ago, frob said:

As for the original problem here on the thread where you're trying to avoid taking the string length, that's not much of a benefit.  You're trying to simplify the interface, but instead you are adding complexity by having an additional entry.  Instead of having only one interface:  (buffer, size), you've now got two interfaces: (buffer, size) and (fixed-length-array).  If the writer knows they've got a fixed array they can use (buffer, sizeof(buffer)). If the writer knows they've got a more traditional buffer they can use (buffer, buflen). They know the single interface is there and they need to use it.

As you should see in my explanation, the function I proposed isn't really going to be part of an interface, its just an additional constructor for my StringView-class that internally calls it. I don't know if that makes it any better in your book, but I do see a compelling case for handling string-literals the way I do. Also the purpose of StringView is to offer a unified interface from many types (std::string, const char*, const char*+size) to a single const char*, size_t-pair. So I'd say my general notion is not totally wrong - the only difference I make is instead of treating every "const char*" as a nul-terminated string, I'm making a differentiation between static string-literals as part of a small optimization.
 

41 minutes ago, frob said:

Imagine if the C language used that in their interfaces.  You'd have the current set of twenty-ish memory functions like memmov, memcpy, memcmp, and a duplicate version of all the functions for fixed-length arrays.

Sure, adding 3-4 overloads for the same functions is surely overkill, I agree on that (in my case I should have mentioned how its intented to being used), but since we are talking about C-API functions - as you can read in my other thread:

there's actually a lot of issues going forward with modern C++ now that most C-style API functions only take nul-terminated C-strings; which wasn't a problem before but now with string_view this is actually limiting its usefulness. So I'd personally rather have atoi(const char*) and atoi(const char*, size_t) than being forced to make sure my strings are nul-terminated... but I thankfully don't have to support a large userbase with my API, so my expertise in that regard is rather limited.

EDIT: Anyways, the suggested "tricks" seem to work, even though for some reason I have to add a template type to my template-class ctor for it to work:

template<typename Type>
class StringView
{
  	template<typename Char, CheckIsCharPointer<Char> = 0>
	BaseStringView(const Char* const& pString) : // still ambigous if I just use "Type" directly
		BaseStringView(pString, StringLength(pString))
	{
	};
  
   template<size_t Length>
   constexpr BaseStringView(DynamicString<Length> pString) :
   BaseStringView(pString, StringLength(pString))
   {
   };
}

But the problem seems solved, so thanks to all for helping me solve the problem :) I'm still rather happy to discuss the issues revolving around this; I just recently started to work with string_view so its certainly good to get more input on it.

Share this post


Link to post
Share on other sites
1 minute ago, Juliean said:

My StringView-constructor can take a static char-array, and directly deduce the size from this

Again, maybe I'm misunderstanding, but you don't know the size of the string this way- you know the size of the array that holds your string.  If your array of characters has a zero anywhere but at the end, your resulting view will have more chars than the length of the text you're giving it.

Share this post


Link to post
Share on other sites
12 minutes ago, trjh2k2 said:

Again, maybe I'm misunderstanding, but you don't know the size of the string this way- you know the size of the array that holds your string.  If your array of characters has a zero anywhere but at the end, your resulting view will have more chars than the length of the text you're giving it.

This could potentially happen, yes. Unless I'm mistaken, this realistically shouldn't happen though:

The key lies in "const char[X]". How do you generate such a type? You could manually declare it, sure, and if someone would go:

const char test[32] = "Test";
const char testing[] = "Test\0ing"; // ... well

Okay, now the StringView says it points to a 32-character long string which should only have 4 characters; and the other one has a \0 manually put int he middle... But outside of that, the only way I can see that you can aquire such a type is by declaring an actual string:

const char test[] = "Test"; // now its fixed
constexpr char constTest[] = "Test";

// all fine
StringView(test);
StringView(constTest);
StringView("Test");

Since you cannot modify the content of a const char[X], I fail to see any other case where you'd end up with what you described. Even if, it would be trivial to check if (strlen == Size) for debug-builts, to rule out the one case I mentioned.

Now what you are thinking about is probably something like this:

char buffer[MAX_PATH];
GetCurrentDirecoty(buffer, MAX_PATH. 0);

StringView(buffer); //uh-oh

Though in this case, as I've said I've simply added a second overload that will be called if you pass in a "char [X]" as opposed to a "const char[X]", and that will actually call strlen.

I mean, it seems pretty obvious to me - but am I missing something? I really cannot think about how else one would realistically create a "const char[X]" type that has the nul-terminator not at the end. Maybe through multiple layers of functions that all take "const char[X]" where someone passes in such a "char buffer[256]", but thats besides what I consider a realistic use-case, in regards to how the StringView-class is being used.

Share this post


Link to post
Share on other sites
5 minutes ago, Juliean said:

Now what you are thinking about is probably something like this:

Yeah, that's mostly what I was thinking.

Share this post


Link to post
Share on other sites

I'm curious *why* you think it is useful to implement this just to elide the strlen() invocation, when your compiler already optimises away strlen() on string constants?

Observe the following C++ program:


#include <cstdio>
#include <cstring>

class IndirectLen {
public:
    IndirectLen(const char *s) : l(strlen(s)) {}

    const long l;
};

int main() {

    const char *name = "foo";

    printf("%ld\n", IndirectLen(name).l);

    return 0;
}

And observe the generated assembly when Clang compiles it:

	.section	__TEXT,__text,regular,pure_instructions
	.macosx_version_min 10, 12
	.globl	_main
	.p2align	4, 0x90
_main:                                  ## @main
	.cfi_startproc
## BB#0:
	pushq	%rbp
Ltmp0:
	.cfi_def_cfa_offset 16
Ltmp1:
	.cfi_offset %rbp, -16
	movq	%rsp, %rbp
Ltmp2:
	.cfi_def_cfa_register %rbp
	leaq	L_.str.1(%rip), %rdi
	movl	$3, %esi
	xorl	%eax, %eax
	callq	_printf
	xorl	%eax, %eax
	popq	%rbp
	retq
	.cfi_endproc

	.section	__TEXT,__cstring,cstring_literals
L_.str.1:                               ## @.str.1
	.asciz	"%ld\n"


.subsections_via_symbols

The compiler was smart enough to not only replace strlen() with a constant, but to elide the entire containing class...

Share this post


Link to post
Share on other sites
22 hours ago, swiftcoder said:

I'm curious *why* you think it is useful to implement this just to elide the strlen() invocation, when your compiler already optimises away strlen() on string constants?

Uhh... you gave part of the answer in your question though: I didn't know that the compiler was even able to do that in the first place :) I just checked if my MSVC-compiler does that too, and in more complex environments, but yeah, seems like this is something pretty basic optimizationwise. Good to know, reduces code-size quite a bit & saves me from further trouble with that kind of stuff. Thanks!

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
  • Advertisement
  • Popular Now

  • Advertisement
  • Similar Content

    • By komires
      We are pleased to announce the release of Matali Physics 4.0, the fourth major version of Matali Physics engine.
      What is Matali Physics?
      Matali Physics is an advanced, multi-platform, high-performance 3d physics engine intended for games, virtual reality and physics-based simulations. Matali Physics and add-ons form physics environment which provides complex physical simulation and physics-based modeling of objects both real and imagined. The engine is available across multiple platforms:
              Android         *BSD         iOS         Linux         OS X         SteamOS         Windows 10 UAP/UWP         Windows 7/8/8.1/10         Windows XP/Vista What's new in version 4.0?
               One extended edition of Matali Physics engine          Support for Android 8.0 Oreo, iOS 11.x and macOS High Sierra (version 10.13.x) as well as support for the latest IDEs          Matali Render 3.0 add-on with physically-based rendering (PBR), screen space ambient occlusion (SSAO) and support for Vulkan API          Matali Games add-on  
      Main benefits of using Matali Physics:
              Stable, high-performance solution supplied together with the rich set of add-ons for all major mobile and desktop platforms (both 32 and 64 bit)         Advanced samples ready to use in your own games         New features on request         Dedicated technical support         Regular updates and fixes
      The engine history in a nutshell
      Matali Physics was built in 2009 as a dedicated solution for XNA. The first complete version of the engine was released in November 2010, and it was further developed to July 2014 forming multi-platform, fully manage solution for .NET and Mono. In the meantime, from October 2013 to July 2014, was introduced simultaneous support for C++. A significant change occurred in July 2014 together with the release of version 3.0. Managed version of the engine has been abandoned, and the engine was released solely with a new native core written entirely in modern C++. Currently the engine is intensively developed as an advanced, cross-platform, high-performance 3d physics solution.
       
      If you have questions related to the latest update or use of Matali Physics engine as a stable physics solution in your projects, please don't hesitate to contact us.

      View full story
    • By JoshKlint_34394
      Today we are pleased to announce the release of Leadwerks Game Engine 4.5.
      Version 4.5 introduces support for VR headsets including the HTC Vive, Oculus Rift, and all OSVR-based hardware, allowing developers to create both room-scale and seated VR experiences. The Leadwerks virtual reality command set is robust yet incredibly simple allowing you to easily convert your existing 3D games into VR titles. To help get you started the source code for our Asteroids3D game has been updated for VR and is now freely available in the Leadwerks Games Showcase.
      Leadwerks Game Engine is uniquely well-suited for VR because of its fast performance, ease of use, and the availability of C++ programming for demanding VR games. Several optimizations for VR have been made including combining the rendering of both eyes into a single culling step. The stability and accuracy of Newton Game Dynamics means we can have in-depth physics interactions in VR.

      A new VR game template has been added to provide common VR features including teleportation locomotion and the ability to pick up and interact with objects in the environment.
      Visual Studio 2017
      We've also upgraded Leadwerks Professional Edition to build with Visual Studio 2017 so you can take advantage of the very latest Visual Studio features. Instructions for upgrading C++ projects from version 4.4 to 4.5 are available here.
      Other Improvements
      Added fog settings in editor and into map file format. New joint scripts and enhancements. Updated to Steamworks 1.41 You can pick up Leadwerks Game Engine with a discount during the Steam Winter Sale.
      About Leadwerks Software
      Leadwerks Software was founded in 2006 to make game development easy and fun. The company launched Leadwerks Game Engine on Steam in January 2014 and has experienced steady growth, now with over 20,000 paid users.  Leadwerks Game Launcher was released as an early access title in September 2015, allowing developers to publish games to Steam Workshop with no submission fee.

      View full story
    • By khawk
      Urho3D 1.7 has been released. The release for the open source, cross-platform 2D/3D game engine includes a number of new features and bug fixes, including new IK support, AppleTV platform support, WebAssembly support, improved font rendering, better integration with Bullet and Box2D, renderer improvements, and more.
      Download the release and view the full changelog at https://urho3d.github.io/releases/2017/08/19/urho3d-1.7-release.html#changelog.
       

      View full story
    • By khawk
      GameDev.net member @Bleys has released a C++ library that may be useful for game developers.

      Called DynaMix, the library:
      You can access the repository at https://github.com/iboB/dynamix and documentation at https://ibob.github.io/dynamix/.
      Learn more from the thread:
      .

      View full story
    • By khawk
      Reddit user Wafflyn made useful Blueprint and C++ cheatsheets for anyone using UE4. From the post:
      UE4 Blueprint Cheat Sheet: http://uecasts.com/resources/unreal-engine-blueprint-cheat-sheet
      UE4 C++ Cheat Sheet: http://uecasts.com/resources/unreal-engine-c-plus-plus-cheat-sheet

      View full story
  • Advertisement