C++ class and method that search values in map - c++

I have written a minimum class to better submit my problem.
I have three files:
1) test.hpp
#include <cstdlib>
#include <stdio.h>
#include <map>
class test {
public:
test () {}
~test () {}
const char *getPin (const char *);
private:
static const std::map<const char *, const char *> pinIndex;
static std::map<const char *, const char *> initializePins ();
};
2) test.cpp
#include "test.hpp"
const std::map<const char *, const char *> test::pinIndex = test::initializePins ();
std::map<const char *, const char *> test::initializePins () {
std::map<const char *, const char *> pins;
pins.insert (
std::pair<const char *, const char *> (
"AAAA", "BBBB"
)
);
return pins;
}
const char *test::getPin (const char *pinNumber) {
if (pinIndex.count (pinNumber) > 0) {
return pinIndex.at (pinNumber);
}
else {
printf ("Undefined pin %s!\n", pinNumber);
exit (EXIT_FAILURE);
}
}
3) main.cpp
#include "test.hpp"
int main () {
test myExample;
const char *a = myExample.getPin ("AAAA");
exit (EXIT_SUCCESS);
}
When I compile and run it I get this error:
Undefined pin AAAA!
If I remove main.cpp and put the main function in test.cpp file, I do not get me any error and GetPin returns the correct value.
Any idea what I'm doing wrong?
Thank you

Your problem is that you're using char* pointers in your map as key values. To find entries in the map, the implementation uses comparison operations (<) for the given key.
If you're going to compare char* pointers those will almost never be the same, and are completely unrelated to their contents, which is what you're actually want to look for.
A simple solution for your problem would be to change your map type to
std::map<std::string, std::string>
Another possible solution is to provide a class that compares two char* pointers based on content as the 3rd template parameter of the map
std::map<char*, char*, MyComparer>
where MyComparer is s.th. like this
struct MyComparer {
bool operator()( const char*& lhs, const char*& rhs ) const {
return strcmp(lhs,rhs) < 0;
}
};
As an explanation why you experience that seemingly inconsistent behavior, when moving your test to a separate compilation unit:
If you have the same string literals (e.g. "AAAA") appearing in the TU multiple times, the compiler can optimize them to be stored just once, and thus you'll have the same address for all of their appearances.

There is no guarantee that "AAAA" will have the same address as "AAAA" used somewhere else in your program. So finding "AAAA" is not guaranteed to work since the map will be comparing pointer values.
Use a std::map<std::string, std::string> instead of std::map<const char *, const char *>, and your issue should be resolved.

Your solution is quite simple:
#include <iostream>
#include <map>
#include <string>
int
main ()
{
std::map<std::string, std::string> pins { { "AAAA" , "BBBB" }, { "CCCC" , "DDDD" } };
auto lookup = pins.find("AAAA");
if (lookup != pins.end())
std::cout << "Pin AAAA found!" << std::endl;
else
std::cerr << "Pin AAAA not found!" << std::endl;
return 0;
}
A couple of suggestions:
Never use c-strings in C++ code. That is not faster but much more heavy to process. The std::string always knows ones length and performs better in comparisons.
Write as few as you can since KISS (keep it simple stupid) in 90% is the best idea.

Related

using an index number, append that index (a character) to char array

I'm attempting to do something like this.
class testClass {
public:
void testFunction(char charArray[])
{
char output[].append(charArray.at(1));
char output[].append(charArray.at(7));
char output[].append(charArray.at(3));
cout << output;
}
int main() {
testClass testObject;
testObject.testFunction("Flowers");
return 0;
}
}
What it's meant to do is:
get the Letters 'F', 'S' and 'O' from the char array from an index number
append that char to the output chararray
It's been frustrating since I've went from strings, to *chars, and char arrays.
Not really sure what the simpliest easiest solution is.
Yes, I want to retain 1 char at a time from that string.
It was just meant to be a fun project but it's way more complicated than I thought it'd be
expected output:
FSO
Do you mean like this:
#include <string>
#include <iostream>
class testClass {
public:
void testFunction(const std::string& charArray)
{
std::string output;
output.push_back(charArray.at(0));
output.push_back(charArray.at(6));
output.push_back(charArray.at(2));
std::cout << output;
}
};
int main() {
testClass testObject;
testObject.testFunction("Flowers");
return 0;
}
Of course C++ like any sane language uses zero-based indexes.

C++ Passing a class method as function pointer

I have a class that has to process data from various files. I thought about creating one function that will read the specified file and then also accept a call back so that it can use that to process the line. Below is an example class to represent what I am trying to do:
#include <iostream>
#include <vector>
#include <string>
class Example
{
std::vector<std::string> m_exampleFileData {
"test1",
"test2",
"test3"
};
public:
void doSomethingMain(const std::string& path)
{
processFile(path, doSomething);
}
private:
void processFile(const std::string& filePath, void (Example::*fpProcessLine)(const std::string&) )
{
for (const auto& line : m_exampleFileData) {
this->*fpProcessLine(line);
}
}
void doSomething(const std::string& line)
{
std::cout << "Hello: " << line << '\n';
}
};
int main(int argc, char** argv) {
const std::string filePath{"path"};
Example ex;
ex.doSomethingMain(filePath);
}
Compiler explorer: https://godbolt.org/z/LKoXSZ
The main issue is that no matter what I do I can't seem to be able to pass the function properly to processFile. Is there a way to do this in C++? How do I go about this?
You need to spell things out explicitly, in this situation:
processFile(path, &Example::doSomething);
Furthermore, you also need to slap on an extra pair of parenthesis, due to operator precedence:
(this->*fpProcessLine)(line);

Class with list initalization of map with function pointers

I'm trying to write a class that will receive a (number of) pair(s) of char arrays (and strings, later down) and function pointers, from which I plan to use the desired function. I'm using a modified version of the code presented in this question for testing purposes, so I have:
#include <iostream>
#include <map>
#include <initializer_list>
using namespace std;
class Test {
std::map<const char*, void(*)()> m_ints;
public:
Test(std::initializer_list<std::pair<const char*, void(*)()>> init):
m_ints(init)
{}
};
void testfunction(){
cout << "This is a test function"<<endl;
}
int main()
{
Test t = { {"hello", &testfunction} };
return 0;
}
Which, when compiled with g++, returns an error:
error: no matching function for call to ‘std::map < const char*, void (\*)()>::map(std::initializer_list < std::pair < const char*, void (*)()> >&)’
m_ints(init)
^
and a VERY long list of notes with candidates. What am I doing wrong, can I initialize my map like this, or should I use an initializer function within the class?
Just like the link you mentioned, you need to const-ify the typename of the key in the initializer-list, e.g.:
std::initializer_list<std::pair<const char* const, void(*)()>>
Notice the key is const char* const and not const char*.
Example
If you don’t want to bother with the painful task of making the type const, you can use the value_type instead, e.g.:
std::initializer_list<decltype(m_ints)::value_type>
Example

RAII char ** to be used as exec argument vector

I'm currently creating an app to launch external apps.
The signature to launch the external apps is:
int launchApp(int argc, char** argv); // argc = amount of arguments, argv = arguments
To add arguments to a std::vector<char *> structure I use the following lambda:
auto addArgument = [](std::vector<char *> & lArguments,
const std::string & sArgument)
{
auto cstr = new char[sArgument.size() + 1];
std::copy(sArgument.cbegin(), sArgument.cend(), cstr);
cstr[sArgument.size()] = '\0';
lArguments.push_back(cstr);
};
And launching an external app:
std::vector<char *> lArguments;
addArgument(lArguments, "Argument 1");
addArgument(lArguments, "Argument 2");
launchApp(lArguments.size(),static_cast<char**>(lArguments.data());
//... Clean up arguments
How would I do this in a RAII manner instead?
I was thinking of using a std::vector<std::vector<char>> instead. However, how can I then pass the underlying raw data (char**) to launchApp()? static_cast<char**>(lArguments.data()) wouldn't work...
I usually do this in two parts:
Build a std::vector<std::string> containing the actual arguments.
Build a std::vector<const char*> where each element is the .data() of the corresponding element in the vector of strings.
The first vector, and the strings contained within it, handle your allocation, resizing, etc. while the second simply acts as an index into the memory that is being managed by the first. The lifetime of your second vector should be shorter than that of the first, which you can guarantee by wrapping both in a class.
Example:
#include <string>
#include <vector>
#include <unistd.h>
int main() {
std::vector<std::string> args = {"echo", "hello", "world"};
std::vector<const char*> argv;
argv.reserve(args.size());
for (auto& arg : args) {
argv.push_back(arg.data());
}
execvp("echo", const_cast<char**>(argv.data()));
}
(Note the ugly const_cast. Depending on how you look at it, this is either because the exec* family of functions don't follow const correctness, or because std::string::data() does not have a non-const version (prior to C++17)).
Or, with the class to wrap it all up nicely:
#include <string>
#include <vector>
#include <unistd.h>
class ExecArguments {
public:
ExecArguments(std::initializer_list<std::string> arguments)
: args(arguments) {
for (auto& arg : args) {
argv.push_back(const_cast<char*>(arg.data()));
}
}
char** data() {
return const_cast<char**>(argv.data());
}
private:
std::vector<std::string> args;
std::vector<char*> argv;
};
int main() {
ExecArguments args{"echo", "hello", "world"};
execvp(args.data()[0], args.data());
}
Collect your parameters as regular strings.
Use an inner class that:
provides a transparent implicit view to a raw array of pointers, and
takes care of managing this pointer array at the same time.
An instance of this inner class is returned by some access method. Its lifetime spans the invoking statement. See below for an example:
#include <iostream>
#include <vector>
class CollectArgs : public std::vector<std::string> {
// just for providing the raw view and taking care of its memory
class RawArgs {
std::vector<char const*> m_argv;
public:
RawArgs(std::vector<char const*> argv) {
std::swap(argv, m_argv);
}
~RawArgs() {}
public:
operator char const* const*() const { return m_argv.data(); }
};
public:
RawArgs raw_args() const {
std::vector<char const*> argv;
for(std::string const &arg : *this) argv.push_back(arg.c_str());
return argv;
}
};
// the application launcher
void call(unsigned argc, char const *const *argv) {
for(unsigned i = 0; i < argc; i++) {
std::cout << argv[i] << std::endl;
}
}
int main() {
CollectArgs args;
args.push_back("Arg1");
args.push_back("Arg2");
// create the raw view and have it destroyed immediately after this statement
call(args.size(), args.raw_args());
}

Sorting strings using qSort

According to this site, I have done the following program which sorts strings.
#include <cstdlib>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char list[5][4]={"dat","mai","lik","mar","ana"};
int main(int argc, char *argv[])
{
int x;
puts("sortirebamde:");
for (x=0;x>sizeof(list)/sizeof(char);x++)
printf("%s\n",list[x]);
qsort(&list,(sizeof(list)/sizeof(char)),sizeof(list[0]),strcmp);
system("PAUSE");
return EXIT_SUCCESS;
}
Here is the error I get
13 C:\Documents and Settings\LIBRARY\Desktop\string_sortireba.cpp invalid conversion from `int (*)(const char*, const char*)' to `int (*)(const void*, const void*)'
13 C:\Documents and Settings\LIBRARY\Desktop\string_sortireba.cpp initializing argument 4 of `void qsort(void*, size_t, size_t, int (*)(const void*, const void*))'
Please help
Please note: It is unusual to store C strings in two dimensional char arrays. It's more normal to have char *ary[], such as argv. That type cannot be sorted directly using qsort and strcmp, because qsort will pass char ** not char * to the comparison function. This is good for efficiency, the pointers can be swapped instead of the whole strings. The Linux manpage for qsort has some good example code with a correct comparison function.
You can't pass strcmp directly to qsort as its comparison function because qsort expects to pass pointers to void where strcmp expects pointers to const char. Given the required similarity between pointers to void and pointers to char, you could probably do it with a cast (for your code), but the cleaner way would be to write a function that takes the right types:
int cmpstr(void const *a, void const *b) {
char const *aa = (char const *)a;
char const *bb = (char const *)b;
return strcmp(aa, bb);
}
Note, however, that in C++ you'd normally want to use std::sort instead of qsort, and probably use std::string instead of char *, which case the sorting gets a lot simpler (and generally faster as well).
The fourth argument of qsort takes 2 void* pointers as args.So you need to define a compare function for yours.
refer to this link for more details.
You can pass strcmp directly to qsort
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char list[5][4]={"dat","mai","lik","mar","ana"};
int main(int argc, char *argv[]) {
int x;
puts("unsorted:");
for (x=0;x<sizeof(list)/sizeof(list[0]);x++)
printf("%s\n",list[x]);
qsort(list,sizeof(list)/sizeof(list[0]),sizeof(list[0]),strcmp);
puts("sorted:");
for (x=0;x<sizeof(list)/sizeof(list[0]);x++)
printf("%s\n",list[x]);
// system("PAUSE");
return EXIT_SUCCESS;
}
use C, not C++
Beyond why qsort fails, don't use it in C++.
#include <algorithm>
#include <iostream>
#include <iterator>
#include <string>
char const* const raw_data[5] = {"dat", "mai", "lik", "mar", "ana"};
std::vector<std::string> data (raw_data, raw_data + 5);
// would rarely be a global
// see below for code that needs to go here
int main() {
using namespace std;
cout << "before: " << data << "\n";
sort(data.begin(), data.end());
cout << "after: " << data << "\n";
return 0;
}
Boost has stream inserter overloads to output a vector directly, but here's one simple version. This goes into a header, rather than being copied and pasted continually:
template<class Stream, class Iter, class Ch>
void write_sequence(Stream& s, Iter begin, Iter end, Ch const* initial, Ch const* sep, Ch const* final) {
if (initial) {
s << initial;
}
if (begin != end) {
s << *begin;
++begin;
for (; begin != end; ++begin) {
if (sep) {
s << sep;
}
s << *begin;
}
}
if (final) {
s << final;
}
}
template<class T, class A>
std::ostream& operator<<(std::ostream& s, std::vector<T,A> const& value) {
write_sequence(s, value.begin(), value.end(), "[", ", ", "]");
return s;
}
More C++-Style - nowadays - with static_cast:
int scmp(const void * s1, const void * s2)
{
const char* _s1 = *static_cast<const char* const*>(s1);
const char* _s2 = *static_cast<const char* const*>(s2);
return strcmp(_s1, _s2);
}
And in main():
char *str_arr[] = { "one", "two", "three" };
qsort(str_arr, sizeof(str_array)/sizeof(char*), sizeof(char*), scmp);
Much easier is it with vector and std::sort.