V: Problem compiling v code with a c++ compiler

Created on 1 May 2020  Â·  9Comments  Â·  Source: vlang/v

V version: 0.1.26/master
OS: Linux test 4.15.0-96-generic #97-Ubuntu SMP Wed Apr 1 03:25:46 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux

What did you do?
VFLAGS="-cc c++" v -cflags '-c -pipe -O3 -W -Wall -Wpointer-arith -Wno-unused-parameter -w -fpermissive -std=gnu++17' -g -show_c_cmd vweb.v
vweb is a custom version of a server which calls out extern c functions exposed from a c++ library

What did you expect to see?
The code should compile correctly even with a c++ compiler

What did you see instead?

==================
/root/.cache/v/vweb.tmp.c: In function ‘int strings__levenshtein_distance(string, string)’:
/root/.cache/v/vweb.tmp.c:1557:79: error: taking address of temporary array
  array_int f = array_repeat(new_array_from_c_array(1, 1, sizeof(int), (int[1]){
                                                                               ^
   0,
   ~~                                                                           
 }), b.len + 1);
 ~                                                                              
/root/.cache/v/vweb.tmp.c: In function ‘string strconv__ftoa__Dec32_get_string_32(strconv__ftoa__Dec32, bool, int, int)’:
/root/.cache/v/vweb.tmp.c:2031:84: error: taking address of temporary array
  array_byte buf = array_repeat(new_array_from_c_array(1, 1, sizeof(byte), (byte[1]){
                                                                                    ^
...
==================

This needs a fix in https://github.com/vlang/vc (v.c)

Bug

Most helpful comment

Just for the record: There seems to be a way to pass the address of a temporary array in C++ (and thus solve this particular issue): std::move(). In Rust's terms it performs something like transfer of ownership. The above example program can be modified to compile both as C and C++:

#include <stdio.h>

#ifdef __cplusplus
#    include <utility>
#    define _MOV std::move
#else
#    define _MOV
#endif

void print_arr(int x[], int len) {
    for (int i = 0; i<len; i++) {
        printf("%d\n", x[i]);
    }
}

int main() {
    print_arr(_MOV((int[]){1, 2, 3}), 3);
}

This program produces the same assembler code when compiled as C and C++ with gcc/g++ 9.3.0 with '-O2'.

All 9 comments

It is a common misconception that C++ is a superset of C. There are features in modern C that do not work in C++ - and addresses of temporary arrays is one of them. Try this C program:

#include <stdio.h>

void print_arr(int x[], int len) {
    for (int i = 0; i<len; i++) {
        printf("%d\n", x[i]);
    }
}

int main() {
    print_arr((int[]){1, 2, 3}, 3);
}

This program works when compiled with a C compiler, but a C++ compiler complains about taking the address of a temporary array.

In C++ you can accomplish similar things by using new(or better std::make_unique). However, whereas the C variant stores the temporary array on the stack, the C++ counterpart stores it on the heap i.e. the memory has to be freed afterwards (that's what smart pointers do automatically).

To make a long story short: C and C++ are different languages. you can't just take C code and compile it with a C++ compiler.

Passing addresses of temporary arrays is used all over the place in generated C.

These are indeed two very different languages, so I think it's best to use 2 different compilers for C and C generated by V, and C++.

It would definitely be nice to make generated C compilable with g++ for simpler integration into a C++ code base, but I'm not sure it's possible.

You can put it on the agenda for a 2.0 or 3.0 release... ;-)

Maybe it could be done with some macros and helper functions but I don't think it's worth it. Particularly because as soon as we say "C++ is supported" expectations will come up that aren't fulfilled.

For the distant future it might be more appropriate to have a C++ output engine that makes use of modern C++ features:

  • map v's string, array, map to STL's std::vector, std::array, std::map
  • map v's generics to C++'s templates
  • use C++'s virtual methods to implement interfaces
  • make use of std::thread (or even C++20's coroutines?) for v's goroutines
  • use C++17's std::optional for v's optional
  • use std::uniqe_ptr, std::shared_ptr, std::weak_ptr for automatic memory management

By making use of these C++ standard features it would be possible to call functions from modern C++-libraries that expect their parameters as STL-conforming objects.

However I think that is a completely new project that takes a lot of time. For now it may be the best to say that currently C++ is officially not supported.

Just for the record: There seems to be a way to pass the address of a temporary array in C++ (and thus solve this particular issue): std::move(). In Rust's terms it performs something like transfer of ownership. The above example program can be modified to compile both as C and C++:

#include <stdio.h>

#ifdef __cplusplus
#    include <utility>
#    define _MOV std::move
#else
#    define _MOV
#endif

void print_arr(int x[], int len) {
    for (int i = 0; i<len; i++) {
        printf("%d\n", x[i]);
    }
}

int main() {
    print_arr(_MOV((int[]){1, 2, 3}), 3);
}

This program produces the same assembler code when compiled as C and C++ with gcc/g++ 9.3.0 with '-O2'.

What a superb suggestion Uwe, I am so angry at myself for not thinking about that. Lovely solution. Let me change my PR to include this code.

isn't this solved?

Yup this is resolved

Was this page helpful?
0 / 5 - 0 ratings

Related issues

radare picture radare  Â·  3Comments

cjmxp picture cjmxp  Â·  3Comments

shouji-kazuo picture shouji-kazuo  Â·  3Comments

choleraehyq picture choleraehyq  Â·  3Comments

arg2das picture arg2das  Â·  3Comments