I have a function that counts the number of occurences of a string in a char array. Calling this function normally with findword(copy, "polar") works perfectly fine and prints an int that's the number of times the string "polar" occurs in the char array "copy". Calling the function via a pthread however gives me compilation issues and I don't know why. This is my first time implementing multithreading.
Here is the function I want to call:
void findword(char *str, string word)
{
char *p;
vector<string> a;
p = strtok(str, " ");
while (p != NULL)
{
a.push_back(p);
p = strtok(NULL, " ");
}
int c = 0;
for (int i = 0; i <= a.size(); i++)
if (word == a[i])
c++;
printf("%d", c);
}
And here is the thread I'm trying to create that is supposed to call the function:
struct findwordargs {
char *str;
string word;
};
struct findwordargs firstwordArguments;
firstwordArguments.str = copy;
firstwordArguments.word = "polar";
pthread_t thread_id = 1;
pthread_create(&thread_id, NULL, findword, (void *)(&firstwordArguments));
pthread_join(thread_id, NULL);
whenever I compile using g++ and the -pthread flag I get this compile error:
error: invalid conversion from ‘int (*)(char*, std::string)’ {aka ‘int (*)(char*, std::__cxx11::basic_string<char>)’} to ‘void* (*)(void*)’ [-fpermissive]
101 | pthread_create(&thread_id, NULL, findword, (void *)(&firstwordArguments));
All the necessary header files are included, thank you for the help.
Your findword() function does not match the signature that pthread_create() requires:
void *(*start_routine)(void *)
Try this instead:
struct findwordargs
{
char *str;
std::string word;
};
void* findword(void *param)
{
findwordargs *args = static_cast<findwordargs*>(param);
std::vector<std::string> a;
char *p = strtok(args->str, " ");
while (p) {
a.push_back(p);
p = strtok(NULL, " ");
}
int c = 0;
for (size_t i = 0; i < a.size(); ++i) {
if (args->word == a[i])
c++;
}
printf("%d", c);
/* alternatively, using more C++-ish routines:
std::istringstream iss(args->str);
std::string word;
while (iss >> word) a.push_back(word);
std::cout << std::count(a.begin(), a.end(), args->word);
*/
return NULL;
}
...
findwordargs firstwordArguments;
firstwordArguments.str = copy;
firstwordArguments.word = "polar";
pthread_t thread_id = 1;
pthread_create(&thread_id, NULL, findword, &firstwordArguments);
pthread_join(thread_id, NULL);
That being said, there is no point in creating a thread just to immediately join it. That blocks the calling thread until the spawned thread exits, which is the same effect as simply calling the function directly, but without the overhead of context switching between threads:
void findword(const std::string &str, const std::string &word)
{
std::vector<std::string> a;
std::istringstream iss(str);
std::string word;
while (iss >> word) a.push_back(word);
std::cout << std::count(a.begin(), a.end(), word);
}
...
findword(copy, "polar");
Is there a reason you're using pthreads instead of std::thread? If so, then ignore me. This is what I would do:
std::thread myThread([=](){ findword(copy, "polar"); });
myThread.join();
-or-
myThread.detach(); // If appropriate.
This depends on something resembling modern C++.
Related
I have a character array like below:
char array[] = "AAAA... A1... 3. B1.";
How can I split this array by the string "..." in Arduino? I have tried:
ptr = strtok(array, "...");
and the output is the following:
AAAA,
A1,
3,
B1
But I actually want output to be
AAAA,
A1,
3.B1.
How to get this output?
edit:
My full code is this:
char array[] = "AAAA... A1... 3. B1.";
char *strings[10];
char *ptr = NULL;`enter code here`
void setup()
{
Serial.begin(9600);
byte index = 0;
ptr = strtok(array, "..."); // takes a list of delimiters
while(ptr != NULL)
{
strings[index] = ptr;
index++;
ptr = strtok(NULL, "..."); // takes a list of delimiters
}
for(int n = 0; n < index; n++)
{
Serial.println(strings[n]);
}
}
The main problem is that strtok does not find a string inside another string. strtok looks for a character in a string. When you give multiple characters to strtok it looks for any of these. Consequently, writing strtok(array, "..."); is exactly the same as writing strtok(array, ".");. That is why you get a split after "3."
There are multiple ways of doing what you want. Below I'll show you an example using strstr. Unlike strtokthe strstr function do find a substring inside a string - just what you are looking for. But.. strstr is not a tokenizer so some extra code is required to print the substrings.
Something like this should do:
int main()
{
char array[] = "AAAA... A1... 3. B1...";
char* ps = array;
char* pf = strstr(ps, "..."); // Find first substring
while(pf)
{
int len = pf - ps; // Number of chars to print
printf("%.*s\n", len, ps);
ps = pf + 3;
pf = strstr(ps, "..."); // Find next substring
}
return 0;
}
You can implement your own split as strtok except the role of the second argument :
#include <stdio.h>
#include <string.h>
char * split(char *str, const char * delim)
{
static char * s;
char * p, * r;
if (str != NULL)
s = str;
p = strstr(s, delim);
if (p == NULL) {
if (*s == 0)
return NULL;
r = s;
s += strlen(s);
return r;
}
r = s;
*p = 0;
s = p + strlen(delim);
return r;
}
int main()
{
char s[] = "AAAA... A1... 3. B1.";
char * p = s;
char * t;
while ((t = split(p, "...")) != NULL) {
printf("'%s'\n", t);
p = NULL;
}
return 0;
}
Compilation and execution:
/tmp % gcc -g -pedantic -Wextra s.c
/tmp % ./a.out
'AAAA'
' A1'
' 3. B1.'
/tmp %
I print between '' to show the return spaces, because I am not sure you want them, so delim is not only ... in that case
Because you tagged this as c++, here is a c++ 'version' of your code:
#include <iostream>
using std::cout;
using std::endl;
#include <vector>
using std::vector;
#include <string>
using std::string;
class T965_t
{
string array;
vector<string> strings;
public:
T965_t() : array("AAAA... A1... 3. B1.")
{
strings.reserve(10);
}
~T965_t() = default;
int operator()() { return setup(); } // functor entry
private: // methods
int setup()
{
cout << endl;
const string pat1 ("... ");
string s1 = array; // working copy
size_t indx = s1.find(pat1, 0); // find first ... pattern
// start search at ---------^
do
{
if (string::npos == indx) // pattern not found
{
strings.push_back (s1); // capture 'remainder' of s1
break; // not found, kick out
}
// else
// extract --------vvvvvvvvvvvvvvvvv
strings.push_back (s1.substr(0, indx)); // capture
// capture to vector
indx += pat1.size(); // i.e. 4
s1.erase(0, indx); // erase previous capture
indx = s1.find(pat1, 0); // find next
} while(true);
for(uint n = 0; n < strings.size(); n++)
cout << strings[n] << "\n";
cout << endl;
return 0;
}
}; // class T965_t
int main(int , char**) { return T965_t()(); } // call functor
With output:
AAAA
A1
3. B1.
Note: I leave changing "3. B1." to "3.B1.", and adding commas at end of each line (except the last) as an exercise for the OP if required.
I looked for a split function and I didn't find one that meets my requirement, so I made one and it works for me so far, of course in the future I will make some improvements, but it got me out of trouble.
But there is also the strtok function and better use that.
https://www.delftstack.com/es/howto/arduino/arduino-strtok/
I have the split function
Arduino code:
void split(String * vecSplit, int dimArray,String content,char separator){
if(content.length()==0)
return;
content = content + separator;
int countVec = 0;
int posSep = 0;
int posInit = 0;
while(countVec<dimArray){
posSep = content.indexOf(separator,posSep);
if(posSep<0){
return;
}
countVec++;
String splitStr = content.substring(posInit,posSep);
posSep = posSep+1;
posInit = posSep;
vecSplit[countVec] = splitStr;
countVec++;
}
}
Llamada a funcion:
smsContent = "APN:4g.entel;DOMAIN:domolin.com;DELAY_GPS:60";
String vecSplit[10];
split(vecSplit,10,smsContent,';');
for(int i = 0;i<10;i++){
Serial.println(vecSplit[i]);
}
String input:
APN:4gentel;DOMAIN:domolin.com;DELAY_GPS:60
Output:
APN:4g.entel
DOMAIN:domolin.com
DELAY_GPS:60
RESET:true
enter image description here
I am trying to write my own operating system. I have followed the tutorials on the OSDev Wiki, and I am now working on writing a console mode, with commands. I need to be able to split a char* into a char**, without all the library functionality (hence freestanding). I have tried iterating through until I meet my delimiter etc, but however I do it, I just get garbage stuck on the end of my first result. What am I doing wrong? This is what I have so far:
static char** splitStr (char* string, char delim) {
char returner[VGA_WIDTH][255];
int loc = 0;
int innerLoc = 0;
for (int i = 0; string[i] != 0x00; i++) {
char c = string[i];
if (c != delim) {
returner[loc][innerLoc] = c;
innerLoc++;
} else {
print ("a string was ");
println (returner[loc]);
innerLoc = 0;
loc++;
}
}
print ("the first string was ");
println (returner[0]);
return (char**)returner;
}
I am asking a question about how to write a specific function in C++ freestanding mode.
void split(const char* str, const char d, char** into)
{
if(str != NULL && into != NULL)
{
int n = 0;
int c = 0;
for(int i = 0; str[c] != '\0'; i++,c++)
{
into[n][i] = str[c];
if(str[c] == d)
{
into[n][i] = '\0';
i = -1;
++n;
}
}
}
}
I'm allocating using calloc to get rid of garbage characters.
EDIT: You should allocate the pointers inside the char** before writing to them.
void allocarr(char** pointers, int bytes, int slots)
{
int i = 0;
while(i <= slots)
{
pointers[i] = (char*)calloc(1, bytes);
++i;
}
}
...
char** sa = (char**)malloc(50*sizeof(char*));
allocarr(sa, 512, 50);
split("Hello;World;", ';', sa);
puts(sa[0]);
I'm getting funky output when running this code. There isn't a compile error. As far as I can tell, the problem is in my getArgs(stringstream& ss, int size) function. The strings are not copying correctly into my char* variables. I wanted a dynamic array of char* to save my command line arguments to. What am I doing wrong here?
#include "stdafx.h"
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
struct arguments
{
int argc; //number of arguments
char **argv; //array of arguments
};
void startupMsg()
{
cout << "******** CS415 SHELL ********" << endl;
}
int countArgs(stringstream& ss)
{
string temp;
int count = 0;
while (ss >> temp)
{
count++;
}
return count;
}
char** getArgs(stringstream& ss, int size)
{
ss.clear();
ss.seekg(0, ios::beg);
char **ary = new char*[size];
for (int i = 0; i < size; i++)
ary[i] = new char;
int c = 0;
string temp;
while (ss >> temp)
{
ary[c] = const_cast<char*>(temp.c_str());
c++;
}
return ary;
}
void printArgs(arguments* args)
{
for (int i = 0; i < args->argc; i++)
{
cout << args->argv[i] << " ";
}
cout << endl;
}
arguments* parseCommand(string command)
{
arguments *args = new arguments;
stringstream ss(command);
args->argc = countArgs(ss);
args->argv = getArgs(ss, args->argc);
return args;
}
int main()
{
string command;
startupMsg();
//while(true)
//{
cout << "user#linux:~$ ";
getline(cin, command);
arguments *args = parseCommand(command);
cout << args->argc << endl;
printArgs(args);
//}
}
Your problem is here:
ary[c] = const_cast<char*>(temp.c_str());
A good rule of thumb is when you find yourself needing const_cast, you're probably doing something wrong. It's not like you never need it, but it's quite an exceptional thing.
In any case, what happens here? Okay, you read into ary[0] a pointer into temp's buffer. Now you get your next argument. Best case, ary[0] and ary[1] now point to the same argument and you've lost the first one. Worst case, temp had to reallocate and now ary[0] is already a dangling pointer.
Regardless, at the end of getArgs(), temp is destroyed, and now all your likely-not-even-different pointers are dangling.
That's bad. You'll need to come up with some different.
I'm studying C/C++ and the exercise I'm doing is to create a program which evaluates an arithmetic expression.
To complete the exercise, I need a general purpose function which is able to tokenize a string.
As the size of the string to parse is not known at compile time, I have to allocate dynamically some data in the heap.
After the work is done, the memory in the heap can be released.
My question is simple: I'm releasing the memory correctly? See the questions in the comments.
Tokenize function
char** Tokenize(const char delimiter, const char* string)
{
const char* pString = string;
char** tokens = new char*[strlen(string)];
char* buffer = new char[strlen(string)];
char* pointer = buffer;
int c = 0;
for (int k = 0; k < strlen(string); k++)
{
if (string[k] == delimiter)
{
buffer[k] = '\0';
tokens[c] = pointer;
pointer = buffer + k + 1;
c++;
continue;
}
buffer[k] = string[k];
}
tokens[c] = nullptr;
return tokens;
}
The main function which tests Tokenize function and relases the heap.
int main()
{
char** tokens = Tokenize('.', "192.168.1.1");
char** startTokensPointer = tokens;
char* startBufferPointer = *tokens;
while (*tokens != nullptr)
{
cout << *tokens << endl;
tokens++;
}
delete[] startTokensPointer; //Releases tokens??
delete[] startBufferPointer; //Releases buffer??
system("PAUSE");
}
You are not deallocating buffer correctly. If none of the chars in string is equal to delimiter the code in this if statement :
if (string[k] == delimiter)
will never be executed and c will remain 0. Then this line :
tokens[c] = nullptr;
will set the first element of tokens that is stored in startBufferPointer to nullptr. In that case you are leaking buffer as the pointer to buffer is "forgotten" in main.
tokens is deallocated correctly in all cases.
Yes, there is no memory leak, but why not use a type that makes it guaranteed?
struct Tokens
{
explicit Tokens(size_t len) : tokens(new char*[len]), buffer(new char[len])
{ }
std::unique_ptr<char*[]> tokens;
std::unique_ptr<char[]> buffer;
};
Tokens Tokenize(const char delimiter, const char* string)
{
auto len = strlen(string);
Tokens result(len);
char* pointer = result.buffer.get();
int c = 0;
for (size_t k = 0; k < len; k++)
{
if (string[k] == delimiter)
{
result.buffer[k] = '\0';
result.tokens[c] = pointer;
pointer = result.buffer.get() + k + 1;
c++;
continue;
}
result.buffer[k] = string[k];
}
result.tokens[c] = nullptr;
return result;
}
int main()
{
auto tok = Tokenize('.', "192.168.1.1");
char** tokens = tok.tokens.get();
while (*tokens != nullptr)
{
cout << *tokens << endl;
tokens++;
}
}
Now all the memory is managed automatically and it's almost impossible to leak.
I have written a function to process a variadic input arguments like below -
std::string ReturnMessage(const char * format, ...)
{
va_list vl;
va_start(vl, format);
std::stringstream ss;
for(int i = 0; format[i] != '\0'; ++i)
{
switch(format[i])
{
case 's':
{
char *str = NULL;
str = va_arg(vl, char *);
if(*str)
{ //Getting memory access violation here
ss << str;
}
}
break;
}
}
val_end(vl);
return ss.str()
}
No when user assigns non string value(ReturnMessage("%s %s","Testing",100)) as string and at that str gets assigned to Bad Pointer and if(*str) statement throws Access Violation exception which is valid here. But How can I prevent invalid string to get added into stringstream??