I am working on an assignment in C++ and am having difficulties with a question.
We are given a function that can take anything in as an argument.We need to call this function, UberFunction, as an int, int*,int**, and char[].
My Code:
void CallUberFunctions() {
// There is a magic function, called "UberFunction" that is capable of taking
// anything for an argument. You need to call "UberFunction" with the
// following types: int, int* int** and char[]
// You also need to call "OtherUberFunction" in the namespace
// "uber_namespace" once, with no arguments.
UberFunction(42);
UberFunction((int*)42);
UberFunction((int**)42);
UberFunction((char[])'*');
}
Here is the error:
home/user/Desktop/cpp_refresher/cpp_refresher.cc:22:20: error: expected '(' for function-style
cast or type construction
I cannot get the char[] type to work properly, is my syntax above incorrect?
Try this fragment:
const int value = 42;
UberFunction(value); // int
UberFunction(&value); // int *
int * p_value = &value;
UberFunction(&p_value); // int * *
static const char text[] = "Hello World!\n";
UberFunction(text); // char []
Note that in the above example, the parameters are changing and not the return value.
Related
I found out that using a C compiler the code below works but not with a C++ compiler. I understand that casting to void** is the correct usage but I can't understand why it compiles with the C compiler even if I use the void* (commented out).
#include <stdio.h>
int fn(void **arg)
{
int *pvalue = *(int**)arg;
*pvalue = 200;
return 0;
}
int main()
{
int value = 99;
int *pvalue = &value;
// fn((void *)&pvalue); // works only in C
// error C2664: 'int fn(void **)': cannot convert argument 1 from 'void *' to 'void **'
fn((void **)&pvalue); // correct, works for both C/C++
printf("%d", value);
return 0;
}
Can someone explain why this is the case?
In C there is allowed to assign a pointer of the type void * to a pointer of other type. This takes place in this call
fn((void *)&pvalue)
where the argument has the type void * that is assigned to the function parameter that has the type void **.
int fn(void **arg)
{
int *pvalue = *(int**)arg;
*pvalue = 200;
return 0;
}
However such an assignment in general is unsafe. For example the value of a pointer of the type void * can not be properly aligned to be assigned to a pointer of other type.
So it was decided to not allow such an assignment in C++ to make programs more safer.
I can't understand why it compiles with the C compiler even if I use the void* (commented out).
It compiles because void* is implicitly convertible to other pointers in C.
fn((void **)&pvalue); // correct, works for both C/C++
This may be well-formed because of the cast, the standard doesn't technically give explicit guarantee that conversion to void** and back yields the same address.
While this may be likely to work in practice, there is no reason to not use void* as the function argument instead, which does have the guarantee. As a bonus, you won't need the cast in the call. Like this:
int fn(void *arg);
fn(&pvalue); // correct, works for both C/C++
Of course, this is assuming type erasure is needed in the first place. Avoid void* when it is not needed.
For avoidance of doubt, there is nothing correct in
fn((void **)&pvalue);
It is just as incorrect as
fn((void *)&pvalue);
The correct way to use the API is to do
int fn(void **arg)
{
int *pvalue = (int *)*arg;
*(int *)pvalue = 200;
return 0;
}
or
int fn(void **arg)
{
*(int *)*arg = 200;
return 0;
}
with
int main()
{
int value = 99;
void *pvalue = (void*)&value;
fn(&pvalue);
printf("%d", value);
return 0;
}
You're not allowed to access an object using any other pointer type, other than the declared type, compatible type, or a character type. Furthermore, while void * is used as a generic pointer type to all sorts of objects in C, there is no generic pointer to a pointer type in C - other than void *!
And this is the reason why the void ** is almost always a sign of a design error in APIs - most usages are just wrong.
There is a statement I saw in an C++ interview test today:
int (*(*fb)(int, char*))[2];
I have no idea what this declaration could mean. It looks much like function pointer but first star and square braces spoil everything.
Visual Studio decodes fb's type as following int[2] * (int, char *) *, which still looks like a bit cryptic.
If we simplify declaration than everything looks clear
int(*(*fa)(int, char*));
int* func(int, char*)
{
return 0;
}
// now we can assign func to fa
fa = func;
Any ideas?
fb is a function pointer of the following signature:
The function takes two parameters: int and char*
The function returns a pointer to an array of two int, which has the type int(*)[2]
Usually, because of the cryptic syntax of function pointers, array pointers and such stuff, you should use typedefs or type aliases (the new using-syntax) to make it clearer step by step:
using int2 = int[2];
using int2ptr = int2*;
using fb = int2ptr(int, char*);
Proof
Also, instead of returning arrays, you could consider returning a std::vector or std::array; instead of passing char pointers you could consider std::string, and instead of using function pointers you could consider std::function. All these are "coulds", since every "raw type" has its reason to exist, but the reasons are very limited.
It is a definition of pointer to function that has two parameters, one of type int and other of type char *, and returns pointer to array of type int[2].
Here is a simplified demonstrative program. I have only changed the second parameter to type const char *
#include <iostream>
int(*f( int x, const char *s ))[2]
{
static int a[2] = { x, *s };
return &a;
}
int main()
{
int (*(*fb)(int, const char*))[2] = f;
auto p = fb( 10, "A" );
std::cout << ( *p )[0] << '\t' << ( char )( *p )[1] << std::endl;
return 0;
}
The output is
10 A
Colleague of mine have just sent an answer:
int (*(*fb)(int, char*))[2];
int(*(returnArray(int, char*)))[2]
{
static int tab[2];
return &tab;
}
// finally we have it
fb = returnArray;
I have no idea who can use this and for what purpose
I have a function which takes a function pointer as an argument, and then calls that function with its own arguments:
typedef int (*my_func_ptr)( int );
int foo( my_func_ptr f ) {
static int i = 0;
return i = f( i );
}
Sometimes, I need to pass functions to foo that depend on more than just integer input to spit out a result.
int add_strlen( int i, const char* s ) {
return i + strlen( s );
}
I could rework the above code to make use of std::function and then use std::bind, but it is preferable to me that these functions be created at compile time, so I'm using templates.
template<const char* S>
int add_strlen( int i ) {
return i + strlen( S );
}
/**
* Usage:
* char bar[] = "bar";
* foo( add_strlen<bar> );
*/
My problem arises when using pointers as template arguments. Whenever I use a pointer to constant data of any type as a template argument, it only manages to compile if the argument being passed is declared as a non-const array of that type.
char char_array[] = "works";
const char const_char_array[] = "error";
char *char_ptr = "error";
const char *const_char_ptr = "error";
The relevant error in Clang (ver. 3.0-6) (errors for char_ptr and const_char_ptr are the same):
func_ptr.cpp:29:9: error: no matching function for call to 'foo'
foo( add_strlen<const_char_array> );
^~~
func_ptr.cpp:6:5: note: candidate function not viable: no overload of 'add_strlen' matching 'my_func_ptr' (aka 'int (*)(int)') for 1st argument
int foo( my_func_ptr f )
Can anyone explain to me why this is? The way I see it, template parameter S is expected to be of type const char*, which in any other circumstance means I can pass in any const or non-const pointer or array of type char and expect it to work. I would like to be able to declare my arrays as const, because I don't want to even imply that they are meant to be modified at runtime. Is there any way to keep my arrays const and use them as template arguments?
Edit: Thanks to some help (and a newer version of Clang with better errors) I was able to determine that supplying a template argument with internal linkage is part of the problem. By declaring the above variables as extern, I am able to use add_strlen<const_char_array> without error. I've also created a simplified test case. It is included below:
#include <cstring>
typedef int (*my_func_ptr)( int );
int foo( my_func_ptr f ) {
static int i = 0;
return i = f( i );
}
template<const char* S>
int add_strlen( int i ) {
return i + strlen( S );
}
extern char char_array[];
extern const char const_char_array[];
extern char *char_ptr;
extern const char *const_char_ptr;
char char_array[] = "foo";
const char const_char_array[] = "bar";
// assigning to string literal is deprecated
char *char_ptr = char_array;
const char *const_char_ptr = "baz";
int main(int argc, const char *argv[])
{
foo( add_strlen<char_array> ); // works
foo( add_strlen<const_char_array> ); // works
//foo( add_strlen<char_ptr> ); // doesn't work
//foo( add_strlen<const_char_ptr> ); // doesn't work
return 0;
}
The error seems to be related to what you are and what you are not allowed to use as non-type template parameters, referring to IBM Linux Compilers documentation for Non-type template parameters they have this to say:
The syntax of a non-type template parameter is the same as a declaration of one of the following types:
integral or enumeration
pointer to object or pointer to function
reference to object or reference to function
pointer to member
The reason why char_array[] and const_char_array[] work when passed in is because they are constant at compile time and will never change underneath the program while it is running. Integral types can be passed in, pointers to integral types however can not be passed in.
The template is expecting a type of const char * a.k.a const char[x], but it is also expecting something that will never change, so the location where the pointer is pointing may never change. When passed in at compiler time your const_char_array it is being passed a char[6] ("error"). The location will never change and the contents will never change. However when passing in the const_char_ptr it is getting a const char *, while the pointer itself may never change, it is entirely possible the location where it points may change. It itself is not static.
char *_arr = new char[20];
const char* _ptr_arr = _arr;
We can agree here that my _ptr_arr is the exact same type as your const_char_ptr, yet the location where the contents are stored may change at run-time. In templates that isn't allowed since it may require a whole new instantiation of the template, and is non-deterministic from when templates are created. A char [6] is static and won't change.
foo( add_strlen<_ptr_arr> );
results in the following compiler error:
test.cpp:36:5: error: no matching function for call to 'foo'
foo( add_strlen<_ptr_arr>);
^~~
test.cpp:6:5: note: candidate function not viable: no overload of 'add_strlen' matching 'my_func_ptr' (aka 'int (*)(int)') for 1st argument
int foo( my_func_ptr f ) {
^
Which is not very helpful, we want to figure out why there is no valid overload, compiling the code with the function stand-alone without being passed as a function pointer we get the following:
add_strlen<_ptr_arr>(0);
will result in:
test.cpp:36:5: error: no matching function for call to 'add_strlen'
add_strlen<_ptr_arr>(0);
^~~~~~~~~~~~~~~~~~~~
test.cpp:16:5: note: candidate template ignored: invalid explicitly-specified argument for template parameter 'S'
int add_strlen( int i ) {
^
So the explicitly-specified argument is invalid, specifically, we can't pass in an pointer to an integral.
Im a little bit confused about passing structs into functions. I understand pointers and everything.
But for instance:
struct stuff
{
int one
int two
};
int main{
stuff fnc;
fnc.two = 2;
fnc.one = 1;
multiply(&fnc);
}
void multiply(const stuff * pm){
cout << pm->one * pm->two;
}
First of all....am i even doing this right.
And second of all, why do we use the address operator when we pass the function, but use the * pointer operator in the actual function call?
Im confused?
Yes, your code is compilable other than the missing semicolons in the defintion of struct stuff. I'm not quite sure exactly what you're asking about passing the function and the actual function call, but I think you're wondering why the function call uses &fnc, but the parameter is stuff *pm? In that case, the fnc variable declared is a plain stuff. It is not a pointer, it refers to the actual instance of that struct.
Now the multiply function is declared as taking a stuff* -- a pointer to a stuff. This means that you can't pass fnc directly -- it's a stuff and multiply expects a *stuff. However, you can pass fnc as a stuff* by using the & operator to take the address, and &fnc is a valid stuff* that can be passed to multiply.
Once you're in the multiply function, you now have a stuff* called pm. To get the one and two variables from this stuff*, you use the pointer to member operator (->) since they are pointers to a stuff and not a plain stuff. After obtaining those values (pm->one and pm->two), the code then multiples them together before printing them out (pm->one * pm->two).
The * and & operands mean different things depending on whether they describe the type or describe the variable:
int x; // x is an integer
int* y = &x; // y is a pointer that stores the address of x
int& z = x; // z is a reference to x
int a = *y; // a in an integer whose value is the deference of y
Your pm variable is declared as a pointer, so the stuff type is modified with *. Your fnc variable is being used (namely for its address), and thus the variable itself is marked with &.
You can imagine the above examples as the following (C++ doesn't actually have these, so don't go looking for them):
int x;
pointer<int> y = addressof(x);
reference<int> z = x;
int a = dereference(y);
It the difference between describing a type and performing an operation.
In
void multiply(const stuff * pm){
cout << pm->one * pm->two;
}
The stuff * pm says that pm is an address of a stuff struct.
The
&fnc
says "the address of fnc".
When a variable is declared like:
stuff *pm;
it tells us that pm should be treated like an address whose underlying type is stuff.
And if we want to get the address of a variable stuff fnc, we must use
&fnc
You need the address of operator so you can get the address of the object, creating a pointer, which the function expects. The '*' in the parameter list is not a pointer operator, it simply says that the variable is a pointer.
Your code is correct. In the main, you successfully create a 'stuff' object and set its values. Then, you pass a constant address to the object into the function multiply. The multiply function then uses that address to access the two variables of the structure to output the multiplication of the variables.
The * in "const stuff * pm" means that it takes a constant pointer to a stuff object.
Here is a working example of what you would like to see.
#include <iostream>
using namespace std;
struct stuff
{
int one;
int two;
};
void multiply(stuff* pm)
{
cout << pm->one * pm->two;
}
int main()
{
stuff* fnc = new stuff;
fnc->two = 1;
fnc->one = 2;
multiply(fnc);
delete fnc;
cin.ignore(1000, 10);
return 0;
}
Sure, this would work, aside from your erroneous main function definition.
The reason why this works is because when you use the unary & operator, it essentially returns a pointer to the operand, so in your case, fnc, which is of type stuff, if you did &fnc, that would return a stuff *. This is why the multiply function must take in a stuff *.
struct stuff
{
int one, two;
};
int main(int argc, const char* argv[]) {
stuff fnc;
fnc.two = 2;
fnc.one = 1;
multiply(&fnc); //passes a pointer to fnc
}
void multiply(const stuff * pm){
//the "*" operator is the multiplication operator, not a pointer dereference
cout << pm->one * pm->two; //->one is equivalent to (*pm).one
}
You have a couple of syntactic errors in your program, but other than that, the basic idea is fine. Here are the syntax problems I had to fix before your program would compile:
#include <iostream>
using namespace std;
struct stuff
{
int one;
int two;
};
void multiply(const stuff * pm) {
cout << pm->one * pm->two;
}
int main() {
stuff fnc;
fnc.two = 2;
fnc.one = 1;
multiply(&fnc);
}
To answer your questions about difference between the '&' (address of) operator and the '*' (pointer dereference) operator though, we just need to think about the types you're passing in to the function.
Take the function multiply:
void multiply(stuff *fnc) {
...
}
In the definition of this function, you are describing something that takes a pointer to a stuff struct. In that first line, you aren't saying you are dereferencing that object, just that you are expecting a pointer to a stuff object.
Now when you call multiply:
stuff fnc;
multiply(&fnc);
You are using the '&' (address of) operator to get a pointer to the object. Since the multiply function expects a pointer, and you have the plain old object, you need to use the & operator to get a pointer to give to the multiply function.
Perhaps it is clearer to think call it like this:
stuff fnc; //The actual object
stuff* fnc_ptr = &fnc; //A pointer to a stuff object, initialized to point at fnc created above
multiply(fnc_ptr); //Call the function with the pointer directly
Following code will tell you about the pointer illustration
A struct address is passed into the function named multiply and this
function perform some operations with the element of the passed
structure and store the result in the result variable.
you can see here clearly that the result variable is previously zero then after passing the address of the structure to the function multiply the result variable value gets updated to value 6. this is how pointer works.
#include <iostream.h>
struct stuff
{
int one;
int two ;
int result;
};
void multiply(stuff *pm);
int main(){
stuff fnc;
fnc.two = 2;
fnc.one = 3;
fnc.result = 0;
multiply(&fnc);
cout<<fnc.result;
return 0;
}
void multiply(stuff *pm)
{
pm->result = pm->one * pm->two;
}
The const modifier in C++ before star means that using this pointer the value pointed at cannot be changed, while the pointer itself can be made to point something else. In the below
void justloadme(const int **ptr)
{
*ptr = new int[5];
}
int main()
{
int *ptr = NULL;
justloadme(&ptr);
}
justloadme function should not be allowed to edit the integer values (if any) pointed by the passed param, while it can edit the int* value (since the const is not after the first star), but still why do I get a compiler error in both GCC and VC++?
GCC: error: invalid conversion from int** to const int**
VC++: error C2664: 'justloadme' : cannot convert parameter 1 from 'int **' to 'const int **'. Conversion loses qualifiers
Why does it say that the conversion loses qualifiers? Isn't it gaining the const qualifier? Moreover, isn't it similar to strlen(const char*) where we pass a non-const char*
As most times, the compiler is right and intuition wrong. The problem is that if that particular assignment was allowed you could break const-correctness in your program:
const int constant = 10;
int *modifier = 0;
const int ** const_breaker = &modifier; // [*] this is equivalent to your code
*const_breaker = & constant; // no problem, const_breaker points to
// pointer to a constant integer, but...
// we are actually doing: modifer = &constant!!!
*modifier = 5; // ouch!! we are modifying a constant!!!
The line marked with [*] is the culprit for that violation, and is disallowed for that particular reason. The language allows adding const to the last level but not the first:
int * const * correct = &modifier; // ok, this does not break correctness of the code