std:: container cast to C style array
-
wrote on 5 Dec 2020, 03:55 last edited by Kent-Dorfman 12 May 2020, 04:31
The reason and the algorithm are not up for debate, but I need to take an array style dynamic C++ container and type cast it appropriately so that it can be used in a POSIX C API function. Consider the following dynamically generated (editable) C++ container:
std::vector<const char*> cmdLine = { "command", "option", "param1", "param2", nullptr };
and the necessity to pass the array to the execvp() call which has the following protoype:
int execvp(const char* pgm, const char* argv[]);
I can find no way to appropriately pass the C++ container as an array of pointers to the execvp() API function. As a consequence of the type of work being done, all warnings and errors MUST be enabled: IOW, no ignoring of unsafe behavior.
Accessing the pointer location of the beginning of the array portion of the container as cmdLine.data() or &cmdLine[0] does not allow cast to (const char* []) in any form that I can determine.
Any thoughts on forcing the cast from *x to x[] ??? Adopted C++11 or C++14 as suported standard.
This didn't work either:
auto cmdLineArray = new const char* [cmdLine.size()]; std::copy(cmdLine.begin(), cmdLine.end(), cmdLineArray); assert(execvp("./CDSTest", *cmdLineArray) >= 0); delete [] cmdLineArray;
ClassTest.cpp: In static member function 'static void Test_CDSShm()::ThreadWrapper::RunProgram(Test_CDSShm()::ThreadWrapper*)': ClassTest.cpp:311:44: error: cannot convert 'const char*' to 'char* const*' assert(execvp("./CDSTest", *cmdLineArray) >= 0); ^~~~~~~~~~~~~
-
The reason and the algorithm are not up for debate, but I need to take an array style dynamic C++ container and type cast it appropriately so that it can be used in a POSIX C API function. Consider the following dynamically generated (editable) C++ container:
std::vector<const char*> cmdLine = { "command", "option", "param1", "param2", nullptr };
and the necessity to pass the array to the execvp() call which has the following protoype:
int execvp(const char* pgm, const char* argv[]);
I can find no way to appropriately pass the C++ container as an array of pointers to the execvp() API function. As a consequence of the type of work being done, all warnings and errors MUST be enabled: IOW, no ignoring of unsafe behavior.
Accessing the pointer location of the beginning of the array portion of the container as cmdLine.data() or &cmdLine[0] does not allow cast to (const char* []) in any form that I can determine.
Any thoughts on forcing the cast from *x to x[] ??? Adopted C++11 or C++14 as suported standard.
This didn't work either:
auto cmdLineArray = new const char* [cmdLine.size()]; std::copy(cmdLine.begin(), cmdLine.end(), cmdLineArray); assert(execvp("./CDSTest", *cmdLineArray) >= 0); delete [] cmdLineArray;
ClassTest.cpp: In static member function 'static void Test_CDSShm()::ThreadWrapper::RunProgram(Test_CDSShm()::ThreadWrapper*)': ClassTest.cpp:311:44: error: cannot convert 'const char*' to 'char* const*' assert(execvp("./CDSTest", *cmdLineArray) >= 0); ^~~~~~~~~~~~~
@Kent-Dorfman said in std:: container cast to C style array:
Accessing the pointer location of the beginning of the array portion of the container as cmdLine.data() or &cmdLine[0] does not allow cast to (const char* []) in any form that I can determine.
Which compilers did you try? This built and ran just fine for me on MinGW 7.3.0 32-bit and MSVC 2019 32-bit:
#include <QDebug> int execvp(const char* pgm, const char* argv[]) { qDebug() << pgm << argv[0] << argv[2]; return 0; } int main(int, char **) { std::vector<const char*> cmdLine = { "command", "option", "param1", "param2", nullptr }; execvp("./CDSTest", cmdLine.data()); }
Console output:
./CDSTest command param1
-
@Kent-Dorfman said in std:: container cast to C style array:
Accessing the pointer location of the beginning of the array portion of the container as cmdLine.data() or &cmdLine[0] does not allow cast to (const char* []) in any form that I can determine.
Which compilers did you try? This built and ran just fine for me on MinGW 7.3.0 32-bit and MSVC 2019 32-bit:
#include <QDebug> int execvp(const char* pgm, const char* argv[]) { qDebug() << pgm << argv[0] << argv[2]; return 0; } int main(int, char **) { std::vector<const char*> cmdLine = { "command", "option", "param1", "param2", nullptr }; execvp("./CDSTest", cmdLine.data()); }
Console output:
./CDSTest command param1
wrote on 5 Dec 2020, 07:29 last edited byg++ -std=c++14 -Wall -Wno-psabi -g -O0 -I../../include -I../../include/linux -I../../include/spw -I../../classes -fconcepts ClassTest.cpp \ -L../../dist -Wl,-rpath=/6TB/home/Q7-common/classes/Util -lZynqUtil -lpthread -lrt -o ClassTest In file included from /usr/include/c++/8/cassert:44, from ClassTest.cpp:27: ClassTest.cpp: In static member function 'static void Test_CDSShm()::ThreadWrapper::RunProgram(Test_CDSShm()::ThreadWrapper*)': ClassTest.cpp:308:56: error: invalid conversion from 'const char**' to 'char* const*' [-fpermissive] assert(execvp("./CDSTest", cmdLine.data()) >= 0); ~~~~~~~~~~~~^~ In file included from /usr/include/boost/config/stdlib/libstdcpp3.hpp:78, from /usr/include/boost/config.hpp:48, from /usr/include/boost/align/detail/addressof.hpp:11, from /usr/include/boost/align/aligned_allocator.hpp:11, from ../../classes/Util/Vectors.hpp:19, from ../../classes/Util/CDSBacking.hpp:20, from ClassTest.cpp:14: /usr/include/unistd.h:578:52: note: initializing argument 2 of 'int execvp(const char*, char* const*)' extern int execvp (const char *__file, char *const __argv[]) ~~~~~~~~~~~~^~~~~~~~ make: *** [Makefile:21: ClassTest] Error 1 [files Util]$ g++ -v Using built-in specs. COLLECT_GCC=g++ COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/8/lto-wrapper OFFLOAD_TARGET_NAMES=nvptx-none OFFLOAD_TARGET_DEFAULT=1 Target: x86_64-linux-gnu Configured with: ../src/configure -v --with-pkgversion='Debian 8.3.0-6' --with-bugurl=file:///usr/share/doc/gcc-8/README.Bugs --enable-languages=c,ada,c++,go,brig,d,fortran,objc,obj-c++ --prefix=/usr --with-gcc-major-version-only --program-suffix=-8 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-bootstrap --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --enable-default-pie --with-system-zlib --with-target-system-zlib --enable-objc-gc=auto --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu Thread model: posix gcc version 8.3.0 (Debian 8.3.0-6)
-
wrote on 5 Dec 2020, 07:33 last edited by
Ugh! closer inspection shows the prototype is
char* const argv[]
,
which may be different than
const char* argv[]
Lemme check!
-
wrote on 5 Dec 2020, 07:43 last edited by
no joy...cannot cast to the correct format of
int execvp(const char *pgm, char *const argv[])
using linux gcc 8.3 (64 bit)
-
Lifetime Qt Championwrote on 5 Dec 2020, 08:38 last edited by Christian Ehrlicher 12 May 2020, 09:27
Add the proper cast and all is fine.
/edit:
std::vector<const char*> cmdLine = { "command", "option", "param1", "param2", nullptr }; main(cmdLine.size() - 1, const_cast<char **>(cmdLine.data())); // or execvp("blub", const_cast<char **>(cmdLine.data()));
Why not using QProcess?
-
no joy...cannot cast to the correct format of
int execvp(const char *pgm, char *const argv[])
using linux gcc 8.3 (64 bit)
wrote on 5 Dec 2020, 08:58 last edited by JonB 12 May 2020, 09:11@Kent-Dorfman
I'm lost :( As per the compiler message,const char**
is not the same type aschar* const*
, andconst char *argv[]
is not the same aschar* const argv[]
. But you can cast-convert between them (as @Christian-Ehrlicher has said) if that's what you want to do, in that if we ignore theconst
s they both physically refer to a pointer-to a pointer-to achar
, or if you prefer an array of pointers-tochar
. So what is your question (meant in the nicest way)? -
Add the proper cast and all is fine.
/edit:
std::vector<const char*> cmdLine = { "command", "option", "param1", "param2", nullptr }; main(cmdLine.size() - 1, const_cast<char **>(cmdLine.data())); // or execvp("blub", const_cast<char **>(cmdLine.data()));
Why not using QProcess?
wrote on 5 Dec 2020, 10:05 last edited by JonB 12 May 2020, 10:05@Christian-Ehrlicher said in std:: container cast to C style array:
Why not using QProcess?
The OP has not shown he is using any
fork()
-type call. Onlyexecvp()
. I don't know whether it's intentional(!), but as it stands he is not creating a sub-process, he is overlaying (i.e. replacing) the current process image with a new process image, no return to calling process.... -
g++ -std=c++14 -Wall -Wno-psabi -g -O0 -I../../include -I../../include/linux -I../../include/spw -I../../classes -fconcepts ClassTest.cpp \ -L../../dist -Wl,-rpath=/6TB/home/Q7-common/classes/Util -lZynqUtil -lpthread -lrt -o ClassTest In file included from /usr/include/c++/8/cassert:44, from ClassTest.cpp:27: ClassTest.cpp: In static member function 'static void Test_CDSShm()::ThreadWrapper::RunProgram(Test_CDSShm()::ThreadWrapper*)': ClassTest.cpp:308:56: error: invalid conversion from 'const char**' to 'char* const*' [-fpermissive] assert(execvp("./CDSTest", cmdLine.data()) >= 0); ~~~~~~~~~~~~^~ In file included from /usr/include/boost/config/stdlib/libstdcpp3.hpp:78, from /usr/include/boost/config.hpp:48, from /usr/include/boost/align/detail/addressof.hpp:11, from /usr/include/boost/align/aligned_allocator.hpp:11, from ../../classes/Util/Vectors.hpp:19, from ../../classes/Util/CDSBacking.hpp:20, from ClassTest.cpp:14: /usr/include/unistd.h:578:52: note: initializing argument 2 of 'int execvp(const char*, char* const*)' extern int execvp (const char *__file, char *const __argv[]) ~~~~~~~~~~~~^~~~~~~~ make: *** [Makefile:21: ClassTest] Error 1 [files Util]$ g++ -v Using built-in specs. COLLECT_GCC=g++ COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/8/lto-wrapper OFFLOAD_TARGET_NAMES=nvptx-none OFFLOAD_TARGET_DEFAULT=1 Target: x86_64-linux-gnu Configured with: ../src/configure -v --with-pkgversion='Debian 8.3.0-6' --with-bugurl=file:///usr/share/doc/gcc-8/README.Bugs --enable-languages=c,ada,c++,go,brig,d,fortran,objc,obj-c++ --prefix=/usr --with-gcc-major-version-only --program-suffix=-8 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-bootstrap --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --enable-default-pie --with-system-zlib --with-target-system-zlib --enable-objc-gc=auto --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu Thread model: posix gcc version 8.3.0 (Debian 8.3.0-6)
@Kent-Dorfman said in std:: container cast to C style array:
Ugh! closer inspection shows the prototype is
char* const argv[]
,
which may be different than
const char* argv[]
Lemme check!
They have different rules:
const char *
: Char data is const. Pointer is non-const.char * const
: Char data is non-const. Pointer is const.
Which means:
std::vector<const char*>
: Char data cannot be modified through the pointer.char* const argv[]
: Char data can be modified through the pointer.
The only way to pass the vector data into your C function is by using a const_cast like @Christian-Ehrlicher showed.
(P.S. I just found out that
execvp()
is part of the standard POSIX API. I'm puzzled as to why the function designer allows the char values to be modified) -
@JKSH said in std:: container cast to C style array:
I'm puzzled as to why the function designer allows the char values to be modified
Maybe for compatibility with main() entry point?
-
@Kent-Dorfman said in std:: container cast to C style array:
Ugh! closer inspection shows the prototype is
char* const argv[]
,
which may be different than
const char* argv[]
Lemme check!
They have different rules:
const char *
: Char data is const. Pointer is non-const.char * const
: Char data is non-const. Pointer is const.
Which means:
std::vector<const char*>
: Char data cannot be modified through the pointer.char* const argv[]
: Char data can be modified through the pointer.
The only way to pass the vector data into your C function is by using a const_cast like @Christian-Ehrlicher showed.
(P.S. I just found out that
execvp()
is part of the standard POSIX API. I'm puzzled as to why the function designer allows the char values to be modified)wrote on 5 Dec 2020, 15:17 last edited by JonB 12 May 2020, 15:18@JKSH , @Christian-Ehrlicher
TBH, I'm puzzled as to why theargv
argument tomain()
is not declaredconst char * const argv[];
-
wrote on 5 Dec 2020, 17:56 last edited by
@Christian-Ehrlicher et al
thanks @Christian-Ehrlicher ... my misstating the problem in the OP led to my own confuction. It was in fact solved by the proper const_cast. due to my misstaing ofthe const char* vs const* char, which were different. No QProcess as it's pure C/C++ ISO with the only externa dependencies to the project being anything that is implemented in pure C++ template headers. Test harness for spacecraft OS and I needed to spawn many clients with different argv params. complete std::thread run method is:
static void RunProgram(ThreadWrapper* instance) { int pid = fork(); if (pid == 0) { static const size_t STRLENGTH = 6; char instanceStr[STRLENGTH]; char cmd[STRLENGTH]; assert(snprintf(instanceStr, STRLENGTH, "%d", instance->instanceNum) > 0); assert(snprintf(cmd, STRLENGTH, "%s", instance->callType.c_str()) > 0); std::vector<const char*> cmdLine = { "./CDSTest", cmd, SHMNAME, nullptr, nullptr }; if (instance->callType == "-c") { cmdLine[3] = instanceStr; } else { cmdLine.resize(4); } assert(execvp("./CDSTest", const_cast<char**>(cmdLine.data())) >= 0); } int status(0); instance->pid = pid; assert(waitpid(pid, &status, 0) >= 0); instance->retval = WEXITSTATUS(status); // stall thread until command completes }
Solved.
-
@Christian-Ehrlicher et al
thanks @Christian-Ehrlicher ... my misstating the problem in the OP led to my own confuction. It was in fact solved by the proper const_cast. due to my misstaing ofthe const char* vs const* char, which were different. No QProcess as it's pure C/C++ ISO with the only externa dependencies to the project being anything that is implemented in pure C++ template headers. Test harness for spacecraft OS and I needed to spawn many clients with different argv params. complete std::thread run method is:
static void RunProgram(ThreadWrapper* instance) { int pid = fork(); if (pid == 0) { static const size_t STRLENGTH = 6; char instanceStr[STRLENGTH]; char cmd[STRLENGTH]; assert(snprintf(instanceStr, STRLENGTH, "%d", instance->instanceNum) > 0); assert(snprintf(cmd, STRLENGTH, "%s", instance->callType.c_str()) > 0); std::vector<const char*> cmdLine = { "./CDSTest", cmd, SHMNAME, nullptr, nullptr }; if (instance->callType == "-c") { cmdLine[3] = instanceStr; } else { cmdLine.resize(4); } assert(execvp("./CDSTest", const_cast<char**>(cmdLine.data())) >= 0); } int status(0); instance->pid = pid; assert(waitpid(pid, &status, 0) >= 0); instance->retval = WEXITSTATUS(status); // stall thread until command completes }
Solved.
wrote on 6 Dec 2020, 08:53 last edited by JonB 12 Jun 2020, 08:56@Kent-Dorfman
I am surprised at your usage ofassert()
. You are using it with a (vital) function call as its argument. Have you tested it, in Release mode, with a variety of compilers? Assuming https://en.cppreference.com/w/cpp/error/assert is "recognised" for C++ ISO (I don't know whether it is), it claims it will indeed be a macro with the definition being like:// Defined in header <cassert> #ifdef NDEBUG #define assert(condition) ((void)0) #else #define assert(condition) /*implementation defined*/ #endif
?
[I have no idea what
spacecraft OS
is, but I'm very worried that yours is going to come crashing onto my head when you launch it non-debug mode ;-) ] -
@Kent-Dorfman
I am surprised at your usage ofassert()
. You are using it with a (vital) function call as its argument. Have you tested it, in Release mode, with a variety of compilers? Assuming https://en.cppreference.com/w/cpp/error/assert is "recognised" for C++ ISO (I don't know whether it is), it claims it will indeed be a macro with the definition being like:// Defined in header <cassert> #ifdef NDEBUG #define assert(condition) ((void)0) #else #define assert(condition) /*implementation defined*/ #endif
?
[I have no idea what
spacecraft OS
is, but I'm very worried that yours is going to come crashing onto my head when you launch it non-debug mode ;-) ]@JonB You're correct - the usage of assert() is wrong in the code from @Kent-Dorfman
-
wrote on 6 Dec 2020, 21:48 last edited by Kent-Dorfman 12 Jun 2020, 21:50
don't read into it. it's a test harness, not flight code. LOL
AUTOSAR standards forbid use of assert in mission critical code anyway so the existence of the assert() in R&D test code is kind of irrelevant...
-
@Kent-Dorfman said in std:: container cast to C style array:
in R&D test code is kind of irrelevant...
It's not irrelevant, it's wrong in your case!
-
don't read into it. it's a test harness, not flight code. LOL
AUTOSAR standards forbid use of assert in mission critical code anyway so the existence of the assert() in R&D test code is kind of irrelevant...
wrote on 7 Dec 2020, 07:53 last edited by JonB 12 Jul 2020, 07:54@Kent-Dorfman said in std:: container cast to C style array:
AUTOSAR standards forbid use of assert in mission critical code anyway so the existence of the assert() in R&D test code is kind of irrelevant...
In that case, even more reason not to put the code you want executed as an argument to
assert()
, only useassert()
on abool
result variable? Anyway, your business, so long as you understand whatassert()
expands to in non-Debug compilation. Your usage just really caught my eye. -
@Kent-Dorfman said in std:: container cast to C style array:
AUTOSAR standards forbid use of assert in mission critical code anyway so the existence of the assert() in R&D test code is kind of irrelevant...
In that case, even more reason not to put the code you want executed as an argument to
assert()
, only useassert()
on abool
result variable? Anyway, your business, so long as you understand whatassert()
expands to in non-Debug compilation. Your usage just really caught my eye.wrote on 8 Dec 2020, 00:44 last edited byThis post is deleted! -
wrote on 8 Dec 2020, 00:46 last edited by
OK...I see the point of contention...since assert is itself defined as a macro...
-
OK...I see the point of contention...since assert is itself defined as a macro...
wrote on 8 Dec 2020, 09:00 last edited by JonB 12 Aug 2020, 09:31@Kent-Dorfman
Indeed so! Hence I pasted the definition, showing that in theNDEBUG
case it's not just that it does not verify the expression result, it's that it does not even evaluate (call) it. So any expression which has any side-effects should not be directly passed as argument toassert()
, your code will not execute theCDSTest
at all! And that will mean the child will continue into thepid != 0
(i.e. parent) code below, and continue running as a forked copy in parallel, with consequences. (IIRC, you should follow anexecvp()
in child with_exit(...)
, just in case....)Hence my fear of your rocket maybe crashing onto my head ;-)
1/20