Function pointer pointed to a member function - c++

I am just learning function pointer and want to test how it works with member functions. The compilation of the following code fails at where it is marked.
# include <iostream>
# include <stdio.h>
using namespace std ;
class TMyClass {
public:
int DoIt ( float a, char b, char c ) {
cout << " TMyClass::DoIt " << endl ;
return a + b + c ;
}
int DoMore ( float a, char b, char c ) {
cout << " TMyClass::DoMore " << endl ;
return a - b + c ;
}
int ( TMyClass::*pt2Member ) ( float, char, char ) ;
int test_function_pointer ( ) {
this->pt2Member = &TMyClass::DoMore ;
int result = ( this -> *pt2Member ) ( 12, 'a', 'b' ) ; // wrong!
// expected unqualified-id before "*" token
return 0 ;
}
} ;
int main () {
TMyClass A ;
A.test_function_pointer () ;
return 0 ;
}
I wonder how to make it work. Thanks!

What a difference a space makes:
int result = ( this ->* pt2Member ) ( 12, 'a', 'b' );
// ^^^
->* is an operator of its own.
See the fixed demo here please.

This line:
int result = ( this -> *pt2Member ) ( 12, 'a', 'b' ) ; // wrong!
should have been:
int result = ( this ->*pt2Member ) ( 12, 'a', 'b' ) ; // wrong!
->* is an operator and you cannot insert spaces inside it as it splits it into two different operators: -> and *. This is similar to <<, >>, --, ++ which also cannot be split by spaces into two different tokens.

Related

Create a streambuf from const char* WITHOUT boost?

Same question as Create a streambuf from const char* except that I can't use boost.
I have to implement an function that takes a const char * as input parameter, and to do so I have to call an other function that takes a istream as input parameter.
Here is a sample of code very simplified:
#include <iostream>
#include <string>
using namespace std ;
void inner_function_I_cant_change ( istream & input ) // the function I must use
{
string s ; // dummy implementation
input >> s ;
cout << s.size() << " : <" << s << ">" << endl ;
}
struct externbuf : streambuf // my own streambuf using a char*...
{
int size ;
bool done ;
char * buffer ;
externbuf ( const char * buffer , int size ) :
size(size),
done(false),
buffer(const_cast<char*>( buffer )) {} // ...that forces me to an ugly const_cast !
int underflow ()
{
if (this->gptr() == this->egptr())
{
if (done) return std::char_traits<char>::eof() ;
this->setg( buffer,buffer,buffer+size ) ;
done = true ;
}
return char_traits<char>::to_int_type( *this->gptr()) ;
}
};
void API_function_I_must_povide ( const char * data , int size ) // the function I must implement
{
externbuf buf( data,size ) ;
istream input( &buf ) ;
inner_function_I_cant_change( input ) ;
}
int main ()
{
API_function_I_must_povide( "bazinga!",8 ) ;
}
This code I works well but I had to do an ugly const_cast !
I tried using a basic_streambuf<const char, char_traits<const char> > instead of a streambuf but I get many errors that I didn't understand well.
Is there a proper way to do it ?
(and, as I said, I can't use boost)
Thanks !
Thanks Remy, your link to Art Of Code made my day!
So, for those who are interested, here my new code, without memcpy and ugly const_cast:
#include <iostream>
#include <string>
using namespace std ;
void inner_function_I_cant_change ( istream & input )
{
char buffer [1000] ;
input.read( buffer,sizeof buffer ) ;
string s1( buffer,input.gcount()) ;
string s2( "hello \0 world !",15 ) ;
if (s1 == s2)
cout << "success!" ;
}
struct externbuf : public streambuf
{
externbuf ( const char * data , unsigned int len ) : begin(data),crt(data),end(data + len) {}
int_type underflow ()
{
return crt == end ? traits_type::eof() : traits_type::to_int_type( *crt ) ;
}
int_type uflow ()
{
return crt == end ? traits_type::eof() : traits_type::to_int_type( *crt++ ) ;
}
int_type pbackfail ( int_type ch )
{
bool cond = crt == begin || (ch != traits_type::eof() && ch != crt[-1]) ;
return cond ? traits_type::eof() : traits_type::to_int_type( *--crt ) ;
}
streamsize showmanyc ()
{
return end - crt ;
}
const char *begin,*crt,*end ;
};
void API_function_I_must_povide ( const char * data , int size )
{
externbuf buf( data,size ) ;
istream input( &buf ) ;
inner_function_I_cant_change( input ) ;
}
int main ()
{
API_function_I_must_povide( "hello \0 world !",15 ) ;
}
Thanks Pete, your solution is development-less but I'm afraid it induces a memcpy from data to the inner buffer of the std::string. And since my buffer may be very big, I try to avoid them as much as possible.

Unpredicted infinite for loop in c++ [duplicate]

This question already has answers here:
How do unsigned integers work
(3 answers)
Closed 2 years ago.
I am writing a program to return first occurrence of the character and the frequency of that character in the string.
For loop in the function is executing infinite times and if condition and block is not executing even once.
What is the problem?
string::size_type find_ch(string &str,char ch,int& i_r)
{
string::size_type first=0;
for(auto i=str.size()-1;i>=0;i--)
{
cout<<"\nInside a for loop."<<endl;
if(str[i]==ch)
{
cout<<"Inside if."<<endl;
first=i+1;
i_r++;
}
}
return first;
}
This loop:
for(auto i = str.size() - 1; i>=0; i--)
will only exit when i is less than 0. But this is not a valid value for an unsigned int. The value will wrap to the maximum unsigned int, and you get an infinite loop.
Note that .size() on a std::string returns a size_t, which is basically an unsigned int type.
One way to fix this would be to cast the return type of .size() to an int, like this:
for(auto i = static_cast<int>(str.size()) - 1; i>=0; i--)
Note that it's important to do the cast before subtracting 1, otherwise you'll get the wrong answer when str is empty.
In c++20, you can avoid this issue entirely by calling the std::ssize() free function, which returns a signed version of the size.
The function definition in general is wrong.
For example if the given character is nit found then why does the function return 0 that is a valid position?
Returning the value first=i+1; will only confuse users of the function. The function shall return std::string::npos if the given character is not found.
Also it is entirely unclear why the loop starts from the end of the string while you need to return the first position of the character.
As for the infinite loop then in the loop there is used variable i that has the unsigned integer type std::string::size_type a value of which never can be negative.
for(auto i=str.size()-1;i>=0;i--)
^^^^^^^^^^^^^^^^^^^
That is the condition i >= 0 is always true by the definition.
The function should be defined the following way
std::pair<std::string::size_type, std::string::size_type> find_ch( const std::string &str, char ch )
{
auto n = str.find( ch );
std::pair<std::string::size_type, std::string::size_type> p( n, 0 );
if ( n != std::string::npos )
{
++p.second;
while ( ( n = str.find( ch, n + 1 ) ) != std::string::npos ) ++p.second;
}
return p;
}
Here is a demonstrative program.
#include <iostream>
#include <string>
#include <utility>
std::pair<std::string::size_type, std::string::size_type> find_ch( const std::string &str, char ch )
{
auto n = str.find( ch );
std::pair<std::string::size_type, std::string::size_type> p( n, 0 );
if ( n != std::string::npos )
{
++p.second;
while ( ( n = str.find( ch, n + 1 ) ) != std::string::npos ) ++p.second;
}
return p;
}
int main()
{
std::string s( "C++ is not the same as C" );
auto p = find_ch( s, 'C' );
if ( p.first != std::string::npos )
{
std::cout << p.first << ": " << p.second << '\n';
}
return 0;
}
The program output is
0: 2
If you are not allowed to use methods of the class std::string then just substitute calls of the method find in the function above to while loops as it is shown below.
#include <iostream>
#include <string>
#include <utility>
std::pair<std::string::size_type, std::string::size_type> find_ch( const std::string &str, char ch )
{
std::pair<std::string::size_type, std::string::size_type> p( std::string::npos, 0 );
std::string::size_type n = 0;
while ( n < str.size() && str[n] != ch ) ++n;
if ( n != str.size() )
{
p.first = n;
++p.second;
while ( ++n != str.size() )
{
if( str[n] == ch ) ++p.second;
}
}
return p;
}
int main()
{
std::string s( "C++ is not the same as C" );
auto p = find_ch( s, 'C' );
if ( p.first != std::string::npos )
{
std::cout << p.first << ": " << p.second << '\n';
}
return 0;
}
Here is an answer similar to #Vlad From Moscow, but uses string functions, and the algorithm std::count.
#include <algorithm>
#include <string>
#include <utility>
#include <iostream>
std::pair<int,int> find_ch(const std::string &str, char ch)
{
std::pair<int, int> ret;
auto pos = str.find_first_of(ch);
if ( pos == std::string::npos )
return {-1,0}; // not found
return { pos, std::count(str.begin() + pos, str.end(), ch) };
}
int main()
{
auto pr = find_ch("abccabc", 'b');
std::cout << "character b is at position " << pr.first << ". Character count is " << pr.second << "\n";
pr = find_ch("abccabc", 'c');
std::cout << "character c is at position " << pr.first << ". Character count is " << pr.second;
}
Output:
character b is at position 1. Character count is 2
character c is at position 2. Character count is 3
Each line of the function basically describes what is being done:
find_first_of the character in the string. If found then return that position and the std::count of that character starting at the first occurrence.
Note the brevity and self-documented way the function is written. A C++ programmer could look at that code and immediately know what it does, due to the names of the functions that are being called.
Writing loops going backwards (as you originally did) with variables incremented here and there, the programmer has to sit down and go through the code to figure out what it is doing, and what the purpose of the function is.

Not sure how to pass parameters from class to constructor in main()

My assignment is to read from a gradbook text file and average every students' program, midterm and final scores, as well as the student ID and the first and last name. So far, code is able to compile and calculates the scores appropriately, but it won't read the studentID and first and last name.
I'm pretty sure the reason why is that the currentStudent variable in main() doesn't have any parameters and makes the constructor use default values. But I'm not sure how to give currentStudent values from class Student while in main(). My best idea for a solution is to move everything from ReadData into main(), but from the description of ReadData from my assignment, I think I have everything I need in there:
"A method called ReadData(istream&) which reads the Student's data. It reads the ID number (integer), first and last names (strings) in that order, the 10 program scores (all integers) and the midterm and exam scores (also integers). It returns true if all data was read successfully, otherwise false"
I'm sorry for the long-winded description, I'm just seeing if I can effectively explain my situation. Any help or advice would be greatly appreciated.
I've only included the class definition, constructor, ReadData, and main below because everything else is just equations and get/sets that I'm pretty sure all work, and I'm trying to lessen what you lovely people would have to read through. If anyone would like to see the full code, I'll post the rest.
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
class Student
{
private:
int studentID;
string firstName,
lastName;
int score[ 10 ];
int midterm, final;
public:
Student ( int, string, string );
bool ReadData ( istream& );
//fstream WriteData ( ostream& ); // I need to clear up with professor first
void setStudentID ( int );
void setFirstName ( string );
void setLastName ( string );
void setMidterm ( int );
void setFinal (int );
const int getStudentID ( );
const string getFirstName ( );
const string getLastName ( );
const int getMidterm ( );
const int getFinal ( );
void setProgramScore ( int, int[ ] );
int getProgramScore ( int );
const double ProgramAvg( );
const double CourseAvg( );
~Student( );
};
Student::Student ( int id = 0, string f = "", string l = "" )
{
setStudentID ( id );
setFirstName ( f );
setLastName ( l );
};
bool Student::ReadData( istream &readStudent )
{
int id;
string first, last;
int x[ 10 ], mid, fin;
readStudent >> id >> first >> last;
for ( int i = 0; i <= 10 - 1; i++ )
{
readStudent >> x [ i ];
setProgramScore( i, x );
}
readStudent >> mid >> fin;
Student studentInfo ( id, first, last );
setMidterm( mid );
setFinal( fin );
if ( readStudent.good( ) )
return true;
else
return false;
};
// getters, setters and calculations in between
int main( )
{
ifstream readStudent;
int lineCount = 0;
double totalProgramAvg = 0
, totalFinalAvg = 0
, totalCourseAvg = 0;
Student currentStudent;
readStudent.open ( "gradebook.txt" );
if ( readStudent.is_open ( ) )
{
while ( currentStudent.ReadData ( readStudent ) == true )
{
totalProgramAvg += currentStudent.ProgramAvg();
totalFinalAvg += currentStudent.getFinal();
totalCourseAvg += currentStudent.CourseAvg();
cout << currentStudent.getStudentID() << " "
<< currentStudent.getFirstName() << " "
<< currentStudent.getLastName() << " ";
for ( int j = 0; j < 10; j++ )
cout << currentStudent.getProgramScore( j ) << " ";
cout << currentStudent.getMidterm() << " "
<< currentStudent.getFinal() << endl;
cout << totalProgramAvg << " " << totalCourseAvg << endl;
lineCount++;
};
readStudent.close( );
cout << lineCount << endl << totalProgramAvg / lineCount << "\n" << totalFinalAvg / lineCount << "\n" << totalCourseAvg / lineCount;
system ("pause");
};
};
bool Student::ReadData( istream &readStudent )
{
int id;
string first, last;
int x[ 10 ], mid, fin;
readStudent >> id >> first >> last;
for ( int i = 0; i <= 10 - 1; i++ )
{
readStudent >> x [ i ];
setProgramScore( i, x );
}
readStudent >> mid >> fin;
Student studentInfo ( id, first, last );
setMidterm( mid );
setFinal( fin );
if ( readStudent.good( ) )
return true;
else
return false;
}; //what?
I haven't checked the rest of your code, but this is certainly erroneous.
You shouldn't be declaring a new item Student studentInfo( id, first, last); You're creating a new item that just dies when the function returns. Instead, you should use id,first,last to modify a current object member you're in, this. You have declared items for this in your class header but then declare local scope variables, use them, create a new student with it, and then all are destroyed when the function returns and they go out of scope. Simply delete/add things where I mark appropriate from the function to get
bool Student::ReadData( istream &readStudent )
{
int x[ 10 ], mid, fin; //if it ain't broke, don't fix it
readStudent >> studentID >> firstName >> lastName; //use your class members that you want to hold that data.
for ( int i = 0; i <= 10 - 1; i++ )
{
readStudent >> x [ i ];
setProgramScore( i, x );
}
readStudent >> mid >> fin;
setMidterm( mid );
setFinal( fin );
if ( readStudent.good( ) )
return true;
else
return false;
}
You can directly access class members in the class function Student::ReadData( istream &readStudent) and you should just do it for all of them but you said the score system was working so I left that alone.
Finally, ; goes after } if it's like a struct, or a class, or a bunch of stuff I don't know, but not a function definition.
Okay, I see another bug /flaw in your project flow:
while ( currentStudent.ReadData ( readStudent ) == true ) { /*stuff*/ } is not going to work right. Your ReadData function is going to read in all the data for your current student, but the while loop is also going to try to do that. I can't fathom the result but it will be ugly no doubt. Your better to use it like this:
if(!(currentStudent.ReadData( readStudent)) {
//Ooops, I failed, what do I do?
}

C++ linked list implementation multiple data insertion

I'm trying to create a subject pre-requisite checker using linked list. I know how to insert single data into a node.
My problem is how to insert multiple data into a node? I found a good example which fits my assignment perfectly. But the problem is I do not understand C very much. Can any one help to explain the void add() function below? I want to use that add function to my assignment.
#include <stdio.h>
#include <conio.h>
#include <malloc.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
struct node
{
char data [ 20 ];
char m [ 5 ] [ 20 ];
int mcount;
struct node * link;
};
struct node * dic [ 26 ];
void add ( char * );
int search ( char * );
void show( );
void deldic( );
void main( )
{
char word [ 20 ] , ch;
int i;
clrscr( );
while ( 1 )
{
clrscr( );
printf ( "\n\t\tDictionary\n" );
printf ( "\n\t\t1.Add Word.\n" );
printf ( "\t\t2.Search Word.\n" );
printf ( "\t\t3.Show Dictionary.\n" );
printf ( "\t\t0.Exit." );
printf ( "\n\n\t\tYour Choice ");
scanf ( "%d", &ch );
switch ( ch )
{
case 1 :
printf ( "\nEnter any word : " );
fflush ( stdin );
gets ( word );
add ( word );
break;
case 2 :
printf ( "\nEnter the word to search : " );
fflush ( stdin );
gets ( word );
i = search ( word );
if ( ! i )
printf ( "Word does not exists." );
getch( );
break;
case 3 :
show( );
getch( );
break;
case 0 :
deldic( );
exit ( 0 );
default :
printf ( "\nWrong Choice" );
}
}
}
void add ( char * str )
{
int i, j = toupper ( str [ 0 ] ) - 65;
struct node * r, * temp = dic [ j ], * q;
char mean [ 5 ] [ 20 ], ch = 'y';
i = search ( str );
if ( i )
{
printf ( "\nWord already exists." );
getch( );
return;
}
q = ( struct node * ) malloc ( sizeof ( struct node ) );
strcpy ( q -> data, str );
q -> link = NULL;
for ( i = 0; tolower ( ch ) == 'y' && i < 5; i++ )
{
fflush ( stdin );
printf ( "\n\nEnter the meaning(s) : " );
gets ( mean [ i ] );
strcpy ( q -> m [ i ] , mean [ i ] );
if ( i != 4 )
printf ( "\nAdd more meanings (y/n) " );
else
printf ( "You cannot enter more than 5 meanings." );
fflush ( stdin );
ch = getche( );
}
q -> mcount = i;
if ( dic [ j ] == NULL || strcmp ( dic [ j ] -> data, str ) > 0 )
{
r = dic [ j ];
dic [ j ] = q;
q -> link = r;
return;
}
else
{
while ( temp != NULL )
{
if ( ( strcmp ( temp -> data, str ) < 0 ) && ( ( strcmp ( temp -> link -> data, str ) > 0 ) ||
temp -> link == NULL ) )
{
q -> link = temp -> link;
temp -> link = q;
return;
}
temp = temp -> link;
}
}
}
Here is my assignment so far
#include <iostream>
#include <string>
#include <iomanip>
using namespace std;
struct subjectlist
{
string subject;
string prereq;
subjectlist *next;
};
subjectlist *start_prt=NULL;
subjectlist *current;
int option=0;
int main ()
{
int x;
string subject;
cout << "1. Add subject" << endl;
cout << "2. Search prerequisite" << endl;
cout << "3. Delete subject" << endl;
cout << "4.Show subjects" << endl;
cout << "5. Save to file" << endl;
cout << "6. Load from file" << endl;
cout << "0. Exit" << endl;
cin >> x;
switch (x)
{
case 1:
cout<<"Input subject"<<endl;
cin >> subject;
add(subject);
break;
case 2:
cout<<"Input subject to be checked"<<endl;
break;
case 3:
cout<<"Delete a subject"<<endl;
break;
case 4:
cout<<"Show Subjects"<<endl;
break;
case 5:
cout<<"Save to File"<<endl;
break;
case 6:
cout<<"Load from file"<<endl;
break;
case 0:
cout<<"exit"<<endl;
break;
default: cout <<"Invalid selection, please try again."<<endl;
}
}
void add ()
{
}
The add() function adds node to list. But before adding node to list, it checks whether the data in node is already present in list?
i = search ( str );
if ( i )
This checks for duplicate data.
If data is already present in list, node is not inserted in list.
If data is not present in list, it moves further.
for ( i = 0; tolower ( ch ) == 'y' && i < 5; i++ )
This for loop accepts meaning (string) in array and only 5 meanings can be added per node.
Also node is added to list in such a way that list will be in sorted form.
Since you are working with c++, a language, what supports object oriented programming, why not use this feature?
First you could create your data structure, what contains all the useful items, you want to store. You could also write an operator== what makes MUCH clearer to compare two Data objects:
struct Data
{
char data [20];
char m [5][20];
int mcount;
bool operator==(const Data& other)const
{
//probably you need more comparisons
return mcount==other.mcount;
}
};
Then you could create a Node class, what holds one of your Data objects, and a pointer to the next (maybe to the previous) item.
struct Node
{
Data data;
Node * next;
//Node * previous;
}
After you got this, you could just create your own linked list class:
class MyLinkedList
{
Node * head;
public:
MyLinkedList(){//initialization steps}
~MyLinkedList(){ //Delete the list}
void add(Data item)
{
if(!contains(item))
{
//append it
}
}
bool contains(Data item){ //... check if the list already contains item}
//create a string representation of the object.
//If you dont like this style, you could also provide
//an operator>> or operator<< for the class
std::string toString()
{
std::stringstream stream;
//iterate through the list, and add elements with
return stream.str();
}
};
If you got this, then in your main() it would look much clearer, what you want:
MyLinkedList list;
Data data; //somehow fill it
//adding items
list.add(data);
//printing the list
cout<<list.toString();
//after it goes out of scope the destructor will be called,
//so you dont need to bother with the deletion.

String and character mapping question for the guru's out there

Here's a problem thats got me stumped (solution wise):
Given a str S, apply character mappings Cm = {a=(m,o,p),d=(q,u),...} and print out all possible combinations using C or C++.
The string can be any length, and the number of character mappings varies, and there won't be any mappings that map to another map (thus avoiding circular dependencies).
As an example: string abba with mappings a=(e,o), d=(g,h), b=(i) would print:
abba,ebba,obba,abbe,abbo,ebbe,ebbo,obbe,obbo,aiba,aiia,abia,eiba,eiia,......
Definitely possible, not really difficult... but this will generate lots of strings that's for sure.
The first thing to remark is that you know how many strings it's going to generate beforehand, so it's easy to do some sanity check :)
The second: it sounds like a recursive solution would be easy (like many traversal problems).
class CharacterMapper
{
public:
CharacterMapper(): mGenerated(), mMapped()
{
for (int i = -128, max = 128; i != max; ++i)
mMapped[i].push_back(i); // 'a' is mapped to 'a' by default
}
void addMapped(char origin, char target)
{
std::string& m = mMapped[origin];
if (m.find(target) == std::string::npos) m.push_back(target);
} // addMapped
void addMapped(char origin, const std::string& target)
{
for (size_t i = 0, max = target.size(); i != max; ++i) this->addMapped(origin, target[i]);
} // addMapped
void execute(const std::string& original)
{
mGenerated.clear();
this->next(original, 0);
this->sanityCheck(original);
this->print(original);
}
private:
void next(std::string original, size_t index)
{
if (index == original.size())
{
mGenerated.push_back(original);
}
else
{
const std::string& m = mMapped[original[index]];
for (size_t i = 0, max = m.size(); i != max; ++i)
this->next( original.substr(0, index) + m[i] + original.substr(index+1), index+1 );
}
} // next
void sanityCheck(const std::string& original)
{
size_t total = 1;
for (size_t i = 0, max = original.size(); i != max; ++i)
total *= mMapped[original[i]].size();
if (total != mGenerated.size())
std::cout << "Failure: should have found " << total << " words, found " << mGenerated.size() << std::endl;
}
void print(const std::string& original) const
{
typedef std::map<char, std::string>::const_iterator map_iterator;
typedef std::vector<std::string>::const_iterator vector_iterator;
std::cout << "Original: " << original << "\n";
std::cout << "Mapped: {";
for (map_iterator it = mMapped.begin(), end = mMapped.end(); it != end; ++it)
if (it->second.size() > 1) std::cout << "'" << it->first << "': '" << it->second.substr(1) << "'";
std::cout << "}\n";
std::cout << "Generated:\n";
for (vector_iterator it = mGenerated.begin(), end = mGenerated.end(); it != end; ++it)
std::cout << " " << *it << "\n";
}
std::vector<std::string> mGenerated;
std::map<char, std::string> mMapped;
}; // class CharacterMapper
int main(int argc, char* argv[])
{
CharacterMapper mapper;
mapper.addMapped('a', "eo");
mapper.addMapped('d', "gh");
mapper.addMapped('b', "i");
mapper.execute("abba");
}
And here is the output:
Original: abba
Mapped: {'a': 'eo''b': 'i''d': 'gh'}
Generated:
abba
abbe
abbo
abia
abie
abio
aiba
aibe
aibo
aiia
aiie
aiio
ebba
ebbe
ebbo
ebia
ebie
ebio
eiba
eibe
eibo
eiia
eiie
eiio
obba
obbe
obbo
obia
obie
obio
oiba
oibe
oibo
oiia
oiie
oiio
Yeah, rather lengthy, but there's a lot that does not directly participate to the computation (initialization, checks, printing). The core methods is next which implements the recursion.
EDIT: This should be the fastest and simplest possible algo. Some may argue with the style or portability; I think this is perfect for an embedded-type thing and I've spent long enough on it already. I'm leaving the original below.
This uses an array for mapping. The sign bit is used to indicate the end of a mapping cycle, so the array type has to be larger than the mapped type if you want to use the full unsigned range.
Generates 231M strings/sec or ~9.5 cycles/string on a 2.2GHz Core2. Testing conditions and usage as below.
#include <iostream>
using namespace std;
int const alphabet_size = CHAR_MAX+1;
typedef int map_t; // may be char or short, small performance penalty
int const sign_bit = 1<< CHAR_BIT*sizeof(map_t)-1;
typedef map_t cmap[ alphabet_size ];
void CreateMap( char *str, cmap &m ) {
fill( m, m+sizeof(m)/sizeof(*m), 0 );
char *str_end = strchr( str, 0 ) + 1;
str_end[-1] = ' '; // space-terminated strings
char prev = ' ';
for ( char *pen = str; pen != str_end; ++ pen ) {
if ( * pen == ' ' ) {
m[ prev ] |= sign_bit;
prev = 0;
}
m[ * pen ] = * pen;
if ( prev != ' ' ) swap( m[prev], m[ *pen ] );
prev = *pen;
}
for ( int mx = 0; mx != sizeof(m)/sizeof(*m); ++ mx ) {
if ( m[mx] == 0 ) m[mx] = mx | sign_bit;
}
}
bool NextMapping( char *s, char *s_end, cmap &m ) {
for ( char *pen = s; pen != s_end; ++ pen ) {
map_t oldc = *pen, newc = m[ oldc ];
* pen = newc & sign_bit-1;
if ( newc >= 0 ) return true;
}
return false;
}
int main( int argc, char **argv ) {
uint64_t cnt = 0;
cmap m;
CreateMap( argv[1], m );
char *s = argv[2], *s_end = strchr( s, 0 );
do {
++ cnt;
} while ( NextMapping( s, s_end, m ) );
cerr << cnt;
return 0;
}
ORIGINAL:
Not as short or robust as I'd like, but here's something.
Requires that the input string always contain the alphabetically first letter in each replacement set
Execute a la maptool 'aeo dgh bi' abbd
Output is in reverse-lexicographical order
Performance of about 22 cycles/string (100M strings/sec at 2.2 GHz Core2)
BUT my platform is trying to be clever with strings, slowing it down
If I change it to use char* strings instead, it runs at 142M strings/sec (~15.5 cycles/string)
Should be possible to go faster using a char[256] mapping table and another char[256] specifying which chars end a cycle.
The map data structure is an array of nodes linked into circular lists.
#include <iostream>
#include <algorithm>
using namespace std;
enum { alphabet_size = UCHAR_MAX+1 };
struct MapNode {
MapNode *next;
char c;
bool last;
MapNode() : next( this ), c(0), last(false) {}
};
void CreateMap( string s, MapNode (&m)[ alphabet_size ] ) {
MapNode *mprev = 0;
replace( s.begin(), s.end(), ' ', '\0' );
char *str = const_cast<char*>(s.c_str()), *str_end = str + s.size() + 1;
for ( char *pen = str; pen != str_end; ++ pen ) {
if ( mprev == 0 ) sort( pen, pen + strlen( pen ) );
if ( * pen == 0 ) {
if ( mprev ) mprev->last = true;
mprev = 0;
continue;
}
MapNode &mnode = m[ * pen ];
if ( mprev ) swap( mprev->next, mnode.next ); // link node in
mnode.c = * pen; // tell it what char it is
mprev = &mnode;
}
// make it easier to tell that a node isn't in any map
for ( MapNode *mptr = m; mptr != m + alphabet_size; ++ mptr ) {
if ( mptr->next == mptr ) mptr->next = 0;
}
}
bool NextMapping( string &s, MapNode (&m)[ alphabet_size ] ) {
for ( string::iterator it = s.begin(); it != s.end(); ++ it ) {
MapNode &mnode = m[ * it ];
if ( mnode.next ) {
* it = mnode.next->c;
if ( ! mnode.last ) return true;
}
}
return false;
}
int main( int argc, char **argv ) {
MapNode m[ alphabet_size ];
CreateMap( argv[1], m );
string s = argv[2];
do {
cerr << s << endl;
} while ( NextMapping( s, m ) );
return 0;
}
The way I would go about this is to create an array of indexes the same length as the string, all initialized at zero. We then treat this array of indexes as a counter to enumerate all the possible mappings of our source string. A 0 index maps that position in the string to the first mapping for that character, a 1 to the second, etc. We can step through them in order by just incrementing the last index in the array, carrying over to the next position when we reach the maximum number of mappings for that position.
To use your example, we have the mappings
'a' => 'e', 'o'
'b' => 'i'
With the input string "abba", we need a four element array for our indexes:
[0,0,0,0] => "abba"
[0,0,0,1] => "abbe"
[0,0,0,2] => "abbo"
[0,0,1,0] => "abia"
[0,0,1,1] => "abie"
[0,0,1,2] => "abio"
[0,1,0,0] => "aiba"
[0,1,0,1] => "aibe"
[0,1,0,2] => "aibo"
[0,1,1,0] => "aiia"
[0,1,1,1] => "aiie"
[0,1,1,2] => "aiio"
[1,0,0,0] => "ebba"
[1,0,0,1] => "ebbe"
[1,0,0,2] => "ebbo"
[1,0,1,0] => "ebia"
[1,0,1,1] => "ebie"
[1,0,1,2] => "ebio"
[1,1,0,0] => "eiba"
[1,1,0,1] => "eibe"
[1,1,0,2] => "eibo"
[1,1,1,0] => "eiia"
[1,1,1,1] => "eiie"
[1,1,1,2] => "eiio"
[2,0,0,0] => "obba"
[2,0,0,1] => "obbe"
[2,0,0,2] => "obbo"
[2,0,1,0] => "obia"
[2,0,1,1] => "obie"
[2,0,1,2] => "obio"
[2,1,0,0] => "oiba"
[2,1,0,1] => "oibe"
[2,1,0,2] => "oibo"
[2,1,1,0] => "oiia"
[2,1,1,1] => "oiie"
[2,1,1,2] => "oiio"
Before we start generating these strings, we're going to need somewhere to store them, which in C, means that we're
going to have to allocate memory. Fortunately, we know the length of these strings already, and we can figure out
the number of strings we're going to generate - it's just the product of the number of mappings for each position.
While you can return them in an array, I prefer to use a
callback to return them as I find them.
#include <string.h>
#include <stdlib.h>
int each_combination(
char const * source,
char const * mappings[256],
int (*callback)(char const *, void *),
void * thunk
) {
if (mappings == NULL || source == NULL || callback == NULL )
{
return -1;
}
else
{
size_t i;
int rv;
size_t num_mappings[256] = {0};
size_t const source_len = strlen(source);
size_t * const counter = calloc( source_len, sizeof(size_t) );
char * const scratch = strdup( source );
if ( scratch == NULL || counter == NULL )
{
rv = -1;
goto done;
}
/* cache the number of mappings for each char */
for (i = 0; i < 256; i++)
num_mappings[i] = 1 + (mappings[i] ? strlen(mappings[i]) : 0);
/* pass each combination to the callback */
do {
rv = callback(scratch, thunk);
if (rv != 0) goto done;
/* increment the counter */
for (i = 0; i < source_len; i++)
{
counter[i]++;
if (counter[i] == num_mappings[(unsigned char) source[i]])
{
/* carry to the next position */
counter[i] = 0;
scratch[i] = source[i];
continue;
}
/* use the next mapping for this character */
scratch[i] = mappings[(unsigned char) source[i]][counter[i]-1];
break;
}
} while(i < source_len);
done:
if (scratch) free(scratch);
if (counter) free(counter);
return rv;
}
}
#include <stdio.h>
int print_each( char const * s, void * name)
{
printf("%s:%s\n", (char const *) name, s);
return 0;
}
int main(int argc, char ** argv)
{
char const * mappings[256] = { NULL };
mappings[(unsigned char) 'a'] = "eo";
mappings[(unsigned char) 'b'] = "i";
each_combination( "abba", mappings, print_each, (void *) "abba");
each_combination( "baobab", mappings, print_each, (void *) "baobab");
return 0;
}
You essentially want to do a depth-first search (DFS) or any other traversal down a directed acyclic word graph (DAWG). I will post some code shortly.
There is a link to the snippets archive which does that, here, Permute2.c. There is another variant of the string permutation (I guess you could then filter out those that are not in the map!) See here on the 'snippets' archive...
Hope this helps,
Best regards,
Tom.
simple, recursive permute, with using char map[256]
char *map [256];
/* permute the ith char in s */
perm (char *s, int i)
{
if (!s) return;
/* terminating condition */
if (s[i] == '\0') {
/* add "s" to a string array if we want to store the permutations */
printf("%s\n", s);
return;
}
char c = s[i];
char *m = map [c];
// printf ("permuting at [%c]: %s\n", c, m);
int j=0;
/* do for the first char, then use map chars */
do {
perm (s, i+1);
s[i] = m[j];
} while (m[j++] != '\0');
/* restore original char here, used for mapping */
s[i] = c;
return;
}
int main ()
{
/* map table initialization */
map['a'] = "eo\0";
map['b'] = "i\0";
map['d'] = "gh\0";
/* need modifyable sp, as we change chars in position, sp="abba" will not work! */
char *sp = malloc (10);
strncpy (sp, "abba\0", 5);
perm (sp, 0);
return 0;
}