Passing non-NULL argv to MPI_Comm_spawn - c++

Suppose that my program (let's call it prog_A) starts as a single MPI process.
And later I want program prog_A to spawn n MPI processes (let's call them prog_B) using MPI_Comm_spawn with the same arguments I passed to prog_A.
For example, if I run prog_A with the arguments 200 100 10
mpiexec -n 1 prog_A 200 100 10
I want prog_B to be provided with the same argments 200 100 10.
How can I do this? I tried the following but it does not work.
char ** newargv = new char*[3];//create new argv for childs
newargv[0] = new char[50];
newargv[1] = new char[50];
newargv[2] = new char[50];
strcpy(newargv[0],argv[1]);//copy argv to newargv
strcpy(newargv[1],argv[2]);
strcpy(newargv[2],argv[3]);
MPI_Comm theother;
MPI_Init(&argc, &argv);
MPI_Comm_spawn("prog_B",newargv,numchildprocs,
MPI_INFO_NULL, 0, MPI_COMM_SELF, &theother,
MPI_ERRCODES_IGNORE);
MPI_Finalize();

Your problem is that you didn't NULL terminate your argv list. Here's the important part of the MPI standard (emphasis added):
The argv argument argv is an array of strings containing arguments
that are passed to the program. The first element of argv is the first
argument passed to command, not, as is conventional in some contexts,
the command itself. The argument list is terminated by NULL in C and
C++ and an empty string in Fortran. In Fortran, leading and trailing
spaces are always stripped, so that a string consisting of all spaces
is considered an empty string. The constant MPI_ARGV_NULL may be used
in C, C++ and Fortran to indicate an empty argument list. In C and
C++, this constant is the same as NULL.
You just need to add a NULL to the end of your list. Here's the corrected code (translated to C since I didn't have the C++ bindings installed on my laptop):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mpi.h"
int main(int argc, char ** argv) {
char ** newargv = malloc(sizeof(char *)*4);//create new argv for childs
int numchildprocs = 1;
MPI_Comm theother;
MPI_Init(&argc, &argv);
MPI_Comm_get_parent(&theother);
if (MPI_COMM_NULL != theother) {
fprintf(stderr, "SPAWNED!\n");
} else {
newargv[0] = (char *) malloc(sizeof(char)*50);
newargv[1] = (char *) malloc(sizeof(char)*50);
newargv[2] = (char *) malloc(sizeof(char)*50);
newargv[3] = NULL;
strncpy(newargv[0],argv[1], 50);//copy argv to newargv
strncpy(newargv[1],argv[2], 50);
strncpy(newargv[2],argv[3], 50);
fprintf(stderr, "SPAWNING!\n");
MPI_Comm_spawn("./prog_B",newargv,numchildprocs,
MPI_INFO_NULL, 0, MPI_COMM_SELF, &theother,
MPI_ERRCODES_IGNORE);
}
MPI_Comm_free(&theother);
MPI_Finalize();
}

You do not need to copy the argument vector at all. All you have to do is make use of the provisions of the C99 standard, which requires that argv should be NULL-terminated:
MPI_Comm theother;
// Passing &argc and &argv here is a thing of the past (MPI-1)
MPI_Init(NULL, NULL);
MPI_Comm_spawn("prog_B", argv+1, numchildprocs,
MPI_INFO_NULL, 0, MPI_COMM_SELF, &theother,
MPI_ERRCODES_IGNORE);
MPI_Finalize();
Note the use of argv+1 in order to skip over the first argument (the program name). The benefit of that code is that it works with any number of arguments passed to the original program.

Related

Weird characters in MPI_File_write

I copied the following example from Using MPI-2: Advanced Features of the Message-Passing Interface but the output file is just weired characters. I tried to change the data types from int to char but the output is still the same. I tried to open the open the outputfile with different programs like Notepadqq and gedit. I tried also to open the file with different file formats and adding null pointer to the end of the file through process zero but the results are still weired characters.
/* example of parallel MPI write into a single file */
#include <stdio.h>
#include "mpi.h"
#define BUFSIZE 100
int main( int argc, char **argv )
{
int i, MyRank, NumProcs, buf[BUFSIZE];
MPI_File TheFile;
MPI_Status status;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &MyRank);
MPI_Comm_size(MPI_COMM_WORLD,&NumProcs);
for (i=0; i<BUFSIZE; i++)
buf[i]=MyRank*BUFSIZE+i;
MPI_File_open(MPI_COMM_WORLD, "testfile",MPI_MODE_CREATE|MPI_MODE_WRONLY,MPI_INFO_NULL, &TheFile);
MPI_File_set_view(TheFile,MyRank*BUFSIZE*sizeof(int),MPI_INT,MPI_INT,"native",MPI_INFO_NULL);
MPI_File_write(TheFile,buf,BUFSIZE,MPI_INT,MPI_STATUS_IGNORE);
// THis is my trial
if(MyRank == 0){
char nullChar = '\0';
MPI_File_write(TheFile, & nullChar , 1 , MPI_CHAR ,MPI_STATUS_IGNORE );
}
MPI_File_close(&TheFile);
MPI_Finalize();
return 0;
}
Because you are opening a binary file and you expect to see meaningful characters (not gonna happen). Look here for the difference between binary and text files. You can always read the data with MPI_File_read().

What is the safe way to create a v8::String from a wchar_t with non-ASCII characters?

I'm writing a Node.js frontend for a DAB development board, which will eventually run on a Raspberry Pi. I am a Java and web developer, and I'm struggling with C++ and converting between different types of strings.
The DAB board comes with a C++ SDK, with a number of handy functions. It allows me to get the number of available programs with GetTotalProgram(). For each program I can call GetProgramName to get the program's name:
GetProgramName(char mode, long dabIndex, char namemode, wchar_t * programName)
... where mode means FM or DAB, namemode means long or short name. The program´s name will be returned in programName.
In order to convert the wchar_t *programName into a v8::String, I found this snippet that I'm using, and understand the basics of:
wchar_t buff[300];
char cbuff[600];
GetProgramName(0, i, 1, buff);
wcstombs( cbuff, buff, wcslen(buff) );
Local<String> str = String::NewFromUtf8(isolate, (const char *) cbuff, v8::String::kNormalString, wcslen(buff));
I iterate through the available programs and build up a v8::Array:
void GetPrograms(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = Isolate::GetCurrent();
HandleScope scope(isolate);
wchar_t buff[300];
char cbuff[600];
int numberOfPrograms, i;
numberOfPrograms = GetTotalProgram();
Local<v8::Array> ARRAY = Array::New(isolate, totalprogram);
for (i = 0; i < numberOfPrograms; i++) {
if (GetProgramName(0, i, 1, buff)) {
wcstombs( cbuff, buff, wcslen(buff) );
Local<String> str = String::NewFromUtf8(isolate, (const char *) cbuff, v8::String::kNormalString, wcslen(buff));
Local<Object> obj = Object::New(isolate);
obj->Set(String::NewFromUtf8(isolate, "name"), str);
ARRAY->Set(i, obj);
}
}
args.GetReturnValue().Set(ARRAY);
}
I call the C++ method from my Node app:
var programs = ext.getPrograms();
for (var i = 0; i < programs.length; i++) {
console.log(programs[i][name]);
}
This mostly works, but when the program's name contains a non ASCII-character, like Æ, Ø, Å, the next elements in ARRAY has a borked name.
Here's what the Node snippet actually outputs (console.log), compared to the expected output:
| ACTUAL | EXPECTED |
| --------- | ---------- |
| NRK SUPER | NRK SUPER |
| NRK VUPER | NRK VÆR |
| NRK P1 ER | NRK P1 |
It seems as though the non-ASCII character causes the next wcstombs to quit early, not copying the later characters.
Why does this happen? Is there a better way to create a v8::String from my wchar_t?
Note:
I have now been able to isolate this problem down to the wcstombs method when running on the Raspberry Pi. The following code:
#include <stdio.h>
#include <string>
#include <cstring>
#include <cstdlib>
char cbuff[600];
wchar_t buff[300] = L"ABCø123abc";
int main( int argc, const char* argv[] ) {
wcstombs( cbuff, buff, wcslen(buff) );
wprintf(L"wcslen of wchar_t array: %u - strlen of char array: %u\n", (char) wcslen(buff), strlen(cbuff));
}
when run on a Mac, outputs
wcslen of wchar_t array: 10 - strlen of char array: 10,
but when run on the Raspberry, outputs
wcslen of wchar_t array: 10 - strlen of char array: 3 - that is, it counts only characters before the ø character
This looks similar to this unanswered question.
WCHAR str[256];0
... // fill str array here
Local<String> v8str = String::NewFromTwoByte(isolate, (const uint16_t *) str);
Notice ::NewFromTwoByte usage instead of ::NewFromUtf8 and (const uint16_t *) cast.
::NewFromTwoByte Allocates a new string from UTF-16 data.
I guess last parameter in wcstombs is the cause of issue. Instead of trying
wcstombs( cbuff, buff, wcslen(buff) );
try
memset(cbuff, 0, sizeof(cbuff));
wcstombs( cbuff, buff, sizeof(cbuff) );
The problem was in the wcstombs( cbuff, buff, wcslen(buff) ) call, which would stop copying characters when it encountered a non-ASCII character. The docs say The behavior of this function depends on the LC_CTYPE category of the selected C locale.
So setting the locale to a UTF-8 variant solved the problem:
setlocale(LC_CTYPE, "C.UTF-8");
Having done this, I can now create v8::Strings this way:
wchar_t buff[300] = L"Something non-ASCII ÆØÅ here";
char cbuff[600];
wcstombs( cbuff, buff, wcslen(buff) );
Local<String> str = String::NewFromUtf8(isolate, (const char *) cbuff, v8::String::kNormalString, wcslen(buff));

"strncpy_s" Not Working

I'm trying to use strncpy_s to characters from one word to an array (I cannot use strncpy in Visual Studio 2013 and I'm totally new to strncpy_s). I keep getting these errors whatever I do:
Error 1 error C2660: 'strncpy_s' : function does not take 3 arguments
Error 2 IntelliSense: no instance of overloaded function "strncpy_s"
matches the argument list argument types are: (char *, char, int)
The purpose of my code is:
If user inputs, for example, "HELLO" (that is, text = HELLO)
Then ->
Copy HELLO to first_array [0]
Copy ELLO to first_array [1]
Copy LLO to first_array [2]
Copy LO to first_array [3]
Copy O to first_array [4]
And here's my code:
int _tmain(int argc, _TCHAR* argv[])
{
char text[32];
cin >> text;
char* first_array[] = {""};
int n = strlen(text);
for (int i = 0; i < n; i++)
{
strncpy_s(first_array[i], text[i], n-i);
}
}
EDIT 1. Modified the code a bit more, now the program runs, but after inputing a text, it suddenly gives me the "example.exe stopped working" error.
int _tmain(int argc, _TCHAR* argv[])
{
char* text[32];
cin >> *text;
char* first_array[] = {""};
//int n = strlen(text);
int n = sizeof(text);
for (int i = 0; i < n; i++)
{
strncpy_s(first_array[i], n - i, text[i], 32);
}
Your code has several issues.
First of all, your call to strncpy_s does not follow the declaration of strncpy_s, which lists four parameters (if the first parameter is a char * as in your case):
errno_t strncpy_s(
char *strDest,
size_t numberOfElements,
const char *strSource,
size_t count
);
But much more importantly, you state that you would like to end up with multiple strings in an array first_array[], each holding a shorter version of the input string than the last. But the first_array[] you declared only holds one char * string, the one you initialized first_array[0] to, which is exactly one character long (the terminating null byte):
char* first_array[] = {""};
Even if you declared it to hold five char * (the initialization is not necessary as you copy the contents over anyway)...
char * first_array[5];
...you still haven't allocated memory space for each of the five char * strings. You just have five pointers pointing nowhere, and would have to allocate memory dynamically, depending on user input.
Because I haven't even talked about what happens if the user enters more than five characters, let alone 32...
At this point, even if I would post "working" code, it would teach you little. You are apparently following some kind of tutorial, or actually attempting to learn by trial & error. I think the right answer here would be:
Get a different tutorial. Even better, get a good book on C or a good book on C++ as online tutorials are notoriously lacking.

Starting a process using posix_spawn

i am using the folowing code to launch the new process in Linux
pid_t processID;
char *argV[] = {"192.168.1.40",(char *) 0};
int status = -1;
status = posix_spawn(&processID,"/home/user/application",NULL,NULL,argV,environ);
if(status == 0)
std::cout<<"Launched Application";
else
std::cout<<"Launching application Failed";
Application did launches but says no command line argument. What is the error in posix_spawn arguments?
From the posix_spawn manual page:
The argument argv is a pointer to a null-terminated array of character pointers to null-terminated character strings. These strings construct the argument list to be made available to the new process. At least argv[0] must be present in the array, and should contain the file name of the program being spawned, e.g. the last component of the path or file argument.
What's happening is that in the launched process, argv[0] will be 192.168.1.40 instead of the name of the executable, and there are no arguments to the program after that.
so change:
char *argV[] = {"192.168.1.40",(char *) 0};
to:
char *argV[] = {"/home/user/application", "192.168.1.40",(char *) 0};
The behaviour of the argv array is mentioned more explicitly later on:
When a program is executed as a result of a posix_spawn() or posix_spawnp() call, it is entered as follows:
main(argc, argv, envp)
int argc;
char **argv, **envp;
where argc is the number of elements in argv (the ''arg count'') and argv points to the array of character pointers to the arguments themselves.

C/C++ Pointers and Arrays help

I have the following C++ code and I can't seem to get it working. What I am trying to do is read numerous entries from the command line, separated by ('|') pipe characters, and then splitting the resulting strings by spaces.
eg.
mkdir C:/unixcode/shells|cd D:/margins/code | pwd| finger kobojunkie | last -l kobojunkie
but so far, I get errors, something about declaring the size of the pointer:
Initializer fails to determine the size of argv2
cannot convert char** to char* for argument 1 to char strtok(char*, const char*)
int main(int argc, char *argv[])
{
char * pch;
pch = strtok (argv,"|");
//parse the contents of the generated arrays
while (pch != NULL)
{
printf ("%s\n",pch);
char * argv2[] = pch;
char * subpch = strtok(argv2," ");
while (subpch !=NULL)
{
printf ("%s\n",subpch);
subpch = strtok (NULL, " ");
}
pch = strtok (NULL, " ");
}
return 0;
}
the type of argv is char**, not char* hence you cannot pass it to strtok. Use argv[ 1 ] instead, but check that argc >= 2 first.
Or, since this is tagged c++, use stl to split the string, eg
void split( const std::string& s, char delim, std::vector<std::string>& elems )
{
std::stringstream ss( s );
std::string item;
while( std::getline( ss, item, delim ) )
if( !item.empty() )
elems.push_back( item );
}
int main( int argc, char *argv[] )
{
if( argc == 2 )
{
std::vector< std::string > elements;
split( argv[ 1 ], '|', elements );
//elements now contains all items..
}
}
The command-line is managed by a program: the shell (probably cmd.exe in Windows or bash in Linux). That shell gets all the stuff written in the command line and parses it and executes the commands specified.
Unless you are writing a shell, you cannot ever see the "|" of your example command-line inside the programs you write. They are effectively processed from the shell and removed from the parameters sent to the programs.
In
mkdir C:/unixcode/shells|cd D:/margins/code | pwd| finger kobojunkie | last -l kobojunkie
the shell calls the 5 following commands, each with the parameters specified
mkdir C:/unixcode/shells
cd D:/margins/code
pwd
finger kobojunkie
last -l kobojunkie
Note none of the programs receive a "|".
If you are indeed writing a shell, the command-line is not available in the argv array. It depends on the way you manage input inside your shell.
argv is an array of arrays pointers. You cannot pass it as is to strtok: you need to pass its elements in a loop
for (k = 1; k < argc; k++) {
pch = strtok(argv[k], "|");
/* ... */
}
Also: are you sure you want to delimit with "|"? That character has a special meaning to shells and, usually, does not make it to your program.
Unless you call your program with them escaped, eg
bash$ ./a.out 'one|two|three' 'four|five|six'
argv is not a string. argv is an array of strings. strtok takes a string, so you cannot pass it an array of strings and expect it to do something meaningful.
Each string element of the argv array is a separate command line parameter, except for the first which is the name of the executable. So what you should be doing is looking through each string entry for "|", and acting accordingly.