4

I want to call a function of a library (that I can not modify)

void function(int* i, char* c);

Is there a way to call the function defining the int and the char on the fly?

i.e. doing something like

function(&1, &'v');

instead of

int int_1 = 1;
char char_1 = 'v';
function(&int_1, &char_v);

This would enormously decrease the length of my code while increasing readability.

dPol
  • 455
  • 4
  • 14
  • 1
    Are you sure about the increasing readability part? – Lawrence Aiello Apr 21 '15 at 16:30
  • How does `function(&1, &'v')` increase readability? – David G Apr 21 '15 at 16:31
  • 1
    try changing pointers to "pointer to const" ? – user3528438 Apr 21 '15 at 16:32
  • 1
    @user3528438 Still wouldn't work -- you can't point to a literal, that makes no conceptual sense. (More correctly, you can't take a pointer to an rvalue, only an lvalue.) – cdhowie Apr 21 '15 at 16:32
  • Commonly, a pointer will be passed if the called routine can/will alter the target of the pointer. Thus, unless the pointer is specifically declared as "pointer-to-const" (e.g. `const int *i`) the assumption is that the called routine can modify what `i` is pointing at - thus, having `char *i` point to a constant value such as `1` would mean that the called routine would be allowed to modify that `1` - which sort of defeats the purpose of having `1` be a constant. – Bob Jarvis - Слава Україні Apr 21 '15 at 16:37
  • As a workaround, you could use templates to give yourself access to constant lvalues on-the-fly, like so: `template struct const_int { static int const value = i; }` and then your call becomes `function(&const_int<1>::value, ...)` (and similar for the `char`). This will only work if the function accepts a pointer-to-constant-`int` though (`int const *`). (You could make the `value` static member non-`const` but then you run the risk of it being modified, and it would be bad for `const_int<1>::value` to suddenly become 42.) – cdhowie Apr 21 '15 at 16:38
  • 1
    overload the function with one taking non-pointer arguments, storing them in temp variables and then call the original function using pointers. – Unimportant Apr 21 '15 at 16:38
  • I get the feeling that some of these solutions are over thinking it. Given that the function is using C-style strings, that part at least can be solved simply by providing `"v"` as an argument. – Peter M Apr 21 '15 at 16:51

6 Answers6

7

As others have noted, the answer is no...

You could simulate it by overloading function :

void function(int i, char c)
{
  function(&i, &c);
}

So now you can write function(1, 'v')

Sean
  • 60,939
  • 11
  • 97
  • 136
  • Wouldn't this be dangerous in case I call `function(1, someCharToBeMofified)`? `someCharToBeMofified`would be expected to be modified but it is not. – SebastianK Apr 21 '15 at 16:45
  • 1
    If the function modifies the data pointed to then the entire question becomes moot. Correct use of const would make these intentions clear. – Unimportant Apr 21 '15 at 17:06
  • 1
    Although @FISOCPP's answer shows that it is possible, this wrapper is the nice way to do it - if it's slathered with explanatory comments. – mskfisher Apr 21 '15 at 17:21
3

Why would you want to do so? Passing a variable as a non const pointer indicates that the callee intends to modify the parameter that is passed therefore it cannot be an rvalue.

So passing a pointer to a literal would be meaningless (unless it is a string literal which is different). Moreover as it is obvious to you, you cannot determine an address of a literal as it is not addressable. The only constant that could be meaningful is a null pointer or some absolute address to an addressable memory

Abhijit
  • 62,056
  • 18
  • 131
  • 204
  • 1
    I've seen C APIs that, for better or for worse, require exactly the OP's behavior. Not that I'd design my code that way. – George Hilliard Apr 21 '15 at 16:36
  • The function provides some advanced functionalities that I am not going to use. Therefore some of the arguments are known at compile time (and even earlier :) ) – dPol Apr 21 '15 at 16:40
  • @PeterM: Agreed, but that is out of scope for this question. – Abhijit Apr 21 '15 at 16:40
  • I disagree that it is out of scope, as for the char part, `function( .., "v")` is valid, which is very close to what the OP wanted to do. It just wastes a char in doing so. – Peter M Apr 21 '15 at 16:43
2

Yes - you can.

function((int*)&(const int &)1, (char*)&(const char &)'v');

And it's completely legal as long as the pointers aren't dereferenced after the function call. This is because they have temporary life-time which equals the full expression in which the function call exists.

They can be used by the function to modify the data without any possible issues.

Life example. Note that the function 'function' isn't defined. The example only demonstrates that such function call is completely valid.

Note: The complexity of this syntax is due to some 'C++' security measures. After all passing a pointer to unnamed data is something you do rare. However this doesn't mean that this structure is illegal or UB.

AnArrayOfFunctions
  • 3,452
  • 2
  • 29
  • 66
  • The two states requirements: reduce the code & improve readability. Not sure you've really met either :-) – Mat Apr 21 '15 at 16:48
  • As for legality: it's not if the called function modifies the pointees. – Mat Apr 21 '15 at 16:49
  • Nope - this is completely legal. I'm sure of it. Had experience with this structure from long time. Also read the ISO standard. However I can't come up with the exact quote now. – AnArrayOfFunctions Apr 21 '15 at 16:58
  • I'll let you find the fine print that says the stripping away of that const and subsequent modification of the value is legal (you certainly can't omit the const), but since a temporary is generated I guess it could "work". – Mat Apr 21 '15 at 17:08
0

FISOCPP's answer is nice, but I don't like the way temporary is created.

It can be done this way with compound lateral syntax:

function(&(int){1}, &(char){'v'});

Both ways that uses temporary cause gcc to emit warnings when you try to take the address, although it's perfectly defined valid code.

Interestingly, as compound lateral has automatic storage in C rather temporary storage in C++, so there won't be even warnings if compiled in C99 mode.

user3528438
  • 2,737
  • 2
  • 23
  • 42
0

You can make this happen in C++11:

#include <type_traits>
#include <iostream>

template <typename Param, typename Arg>
Param take_address_if_necessary_impl (Arg&& arg, std::true_type, std::false_type)
{
    return arg;
}

template <typename Param, typename Arg>
Param take_address_if_necessary_impl (Arg&& arg, std::false_type, std::true_type)
{
    return &arg;
}

template <typename Param, typename Arg>
Param take_address_if_necessary (Arg&& arg)
{
    return take_address_if_necessary_impl <Param> (
        arg,
        typename std::is_convertible <Arg, Param>::type {},
        typename std::is_convertible <typename std::add_pointer <Arg>::type, Param>::type {}
    );
}

template <typename Ret, typename... Params, typename... Args>
Ret call_special (Ret (*f) (Params...), Args&&... args)
{
    return f (take_address_if_necessary <Params, Args> (args)...);
}

template <typename... Params, typename... Args>
void call_special (void (*f) (Params...), Args&&... args)
{
    f (take_address_if_necessary <Params> (args)...);
}

void function (int* i, char* c)
{
    std::cout << *i << ' ' << *c << std::endl;
}

int main ()
{
    int i = 42;
    char c = '%';

    call_special (function, 1, 'f');
    call_special (function, &i, '?');
    call_special (function, &i, &c);
}

The above program yields

1 f
42 ?
42 %

as you'd expect.

There are some caveats here: first, this will fail if you try to use an overloaded function, because C++ can't deduce an overloaded function to a function pointer:

void bar (int);
void bar (float);

call_special (bar, 3.0f); // Compiler error

You might be able to fix this with explicit template arguments:

call_special <float> (bar, 3.0f); // Works

Or of course explicitly typing the function:

call_special ((void (*) (float))bar, 3.0f);

Second, for simplicity's sake, call_special and its helpers play fast and loose with value classes. It may need more work to be able to handle rvalues, const values, etc. robustly.

Third, arguments that can be passed both as values and as pointers will not work. In fact, conversions in general are probably going to cause headaches:

void quux (long* i)
{
    if (i)
        std::cout << *i << std::endl;
    else
        std::cout << "(null)" << std::endl;
}

call_special (quux, NULL); // What does this print?

You may be better off using a function to grab the address explicitly:

template <typename T>
T* foo (T&& t)
{
    return &t;
}

function (foo (3), foo ('7'));

Note that whatever method you use, you're going to be dealing with temporaries, which die at the semicolon. So if the library you're using stores a reference to an argument you give it, you have no choice but to explicitly give storage to the argument.

Stuart Olsen
  • 476
  • 2
  • 7
0

Something that can be assigned to or have its address taken is an lvalue.

Something that cannot have its address taken, nor be assigned to (sortof), is an rvalue.

This is a function that takes an rvalue, and converts it to an lvalue. This lvalue will only be valid as long as the source rvalue was valid, which hopefully is long enough:

template<class T>
T& as_lvalue(T&& t){return t;}
template<class T>
void as_lvalue(T&)=delete;

We then use it as follows:

function(&as_lvalue(1), &as_lvalue('v'));

as_lvalue is roughly the opposite operation of std::move, which could otherwise be called as_rvalue. It isn't completely the opposite, because I made lvalue-to-lvalue conversion generate an error.

Now, unary & can be overloaded. So we can write:

template<class T>
T* addressof_temporary(T&& t) {
  return std::addressof( as_lvalue(std::forward<T>(t)) );
}

then call:

function(addressof_temporary(1), addressof_temporary('v'));

if paranoid.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524