Juliean

C++ Char pointer/array type ambiguity

Recommended Posts

Juliean    7068

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
trjh2k2    416

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
Juliean    7068
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
trjh2k2    416

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
Juliean    7068
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
NajeNDa    120

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
frob    44911

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
trjh2k2    416

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
Juliean    7068
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
trjh2k2    416
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
Juliean    7068
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
swiftcoder    18432

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
Juliean    7068
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
_Silence_    968
On 09/08/2017 at 5:29 PM, Juliean said:

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*

Read this.

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


  • Similar Content

    • By MarcusAseth
      I need some help to understand what I'm doing wrong here x_x
      Here's my step:
      1) create a new C++ project with starting content, drop a door in the scene.
      2)Add a C++ component to the door called OpenDoor
      3)add a variable in the .h and initialize it in the .cpp  (code below)
      When I compile this, the editor crash and any future attempt to open the project won't succede. What mistake did I made? Furthermore, if said mistake is made, is the project lost forever or there is a way to restore it? x_x  Cause if wathever silly mistake I've made, if it's all it takes to corrupt and lose an entire project, then I'm done with Unreal Editor... x_x
      OpenDoor.h:
      // Fill out your copyright notice in the Description page of Project Settings. #pragma once #include "CoreMinimal.h" #include "Components/ActorComponent.h" #include "OpenDoor.generated.h" UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) ) class BUILDINGESCAPE_API UOpenDoor : public UActorComponent { GENERATED_BODY() public: // Sets default values for this component's properties UOpenDoor(); protected: // Called when the game starts virtual void BeginPlay() override; public: // Called every frame virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override; private: float DoorYaw; };  
      OpenDoor.cpp:
      // Fill out your copyright notice in the Description page of Project Settings. #include "OpenDoor.h" #include "GameFramework/Actor.h" // Sets default values for this component's properties UOpenDoor::UOpenDoor() :DoorYaw{GetOwner()->GetActorRotation().Yaw} { // Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features // off to improve performance if you don't need them. PrimaryComponentTick.bCanEverTick = true; // ... } // Called when the game starts void UOpenDoor::BeginPlay() { Super::BeginPlay(); // ... } // Called every frame void UOpenDoor::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) { Super::TickComponent(DeltaTime, TickType, ThisTickFunction); // ... }  
    • By MarcusAseth
      I'm getting a "red minus icon" next to all my .h and .cpp inside of the VS 2017 solution explorer, anyone knows what does it means? It is red so it doesn't mean anything good, right?
    • By povilaslt2
      Hello. I'm Programmer who is in search of 2D game project who preferably uses OpenGL and C++. You can see my projects in GitHub. Project genre doesn't matter (except MMO's :D).
    • By King Mir
      I'm trying to find a good precise numeric solution for my problem.
      Background:
      I'm trying to create a trade simulation between cities. Each demands a certain amount of a resource and may have a certain number of importers cities that provide that resource. A city needs to take an equal fraction of each importer's resources, and the sum of the amounts taken must add up to the amount demanded. For example, if a city demands 2 bushels of grain, and has three importers, it will take 2/3 of the resources form each importer city, and the sum must add up to 2. Cities will always produce and demand an integer amount of goods, but a city may export to multiple places, so the amount of goods available may be a rational number. At no point are irrational numbers needed.
      Limitations:
      I'm working in C++ and emscripten. This means I cannot link any library to my project; I must include the whole source. For this reason, I'd prefer not to include any large library. I don't want to use boost. This is a closed source project, so I can't use anything GPL.
      Options:
      1) Use floating point with an epsilon. But I'm not sure how to pick the right epsilon here. This reproach has the advantage of being fast and simple though.
      2) Use fixed point. If I use scaling factor that's a multiple or several lowest factors, it may be precise. But I need to ensure that any numerator of my fractions is a factor of the scaling factor. I'm not sure if I can ensure this. But if a city imports from at most N cites, and the scaling factor is N!, maybe this could work? or some version of it?
      3) Use rational numbers. This ensure that calculations are precise all around. If I implement this myself, this is the most complex option.

      The Ask:
      I'm wondering if anyone here has any suggestions as to which is the best option for my use case, and if there are any libraries I might be able to use.
    • By tyhender
      Hello, my name is Mark. I'm hobby programmer. 
      So recently,I thought that it's good idea to find people to create a full 3D engine. I'm looking for people experienced in scripting 3D shaders and implementing physics into engine(game)(we are going to use the React physics engine). 
      And,ye,no money =D I'm just looking for hobbyists that will be proud of their work. If engine(or game) will have financial succes,well,then maybe =D
      Sorry for late replies.
      I mostly give more information when people PM me,but this post is REALLY short,even for me =D
      So here's few more points:
      Engine will use openGL and SDL for graphics. It will use React3D physics library for physics simulation. Engine(most probably,atleast for the first part) won't have graphical fron-end,it will be a framework . I think final engine should be enough to set up an FPS in a couple of minutes. A bit about my self:
      I've been programming for 7 years total. I learned very slowly it as "secondary interesting thing" for like 3 years, but then began to script more seriously.  My primary language is C++,which we are going to use for the engine. Yes,I did 3D graphics with physics simulation before. No, my portfolio isn't very impressive. I'm working on that No,I wasn't employed officially. If anybody need to know more PM me. 
       
  • Popular Now