My problem is how to use correctly the function infile.open().
I have a class that, among the others, has the following public properties:
class myclass {
public:
int rows
int columns
const char* array_file
}
All values are given at run-time.
When I call the function that uses a member of the class I have (pt is a pointer to a member of the class)
#include <vector>
#include <fstream>
#include <iostream>
typedef std::vector< std::vector<int> > Matrixint;
void function(myclass* pt) {
Matrixint array_name(pt->rows, std::vector<int>(pt->columns));
std::ifstream infile;
infile.open("%s", pt->array_file); // my problem: is this correct?
for (int a = 0; a < pt->rows; a++) {
for (int b = 0; b < pt->columns; b++) {
infile >> array_name[a][b] ;
}
}
infile.close();
}
Is this way of opening/reading the file correct?
The data in the file will be formatted as in this question (please note: only the array will be present in the file, no other data)
infile.open gets as its first parameter the filename:
void open ( const char * filename, ios_base::openmode mode = ios_base::in );
(source)
I don't know what your filename is but maybe something like this (just a guess based on variable types) could do:
infile.open(pt->array_file);
of course you have to ensure that the filename you pass in is correct at the time of calling that function.
Assuming that my psychic abilities for fixing your question's code were right, I'd write it like this:
struct mine {
int rows
int columns
std::string array_file
}
void function(const mine& m) {
Matrixint array_name(pt->rows, std::vector<int>(pt->columns));
std::ifstream infile(m.array_file.c_str());
for (int a = 0; a < ls->rows && infile.good(); ++a) {
for (int b = 0; b < ls->columns && infile.good(); ++b) {
infile >> array_name[a][b] ;
}
}
if(!infile)
throw "Uh oh!"; // assume real error handling here
}
Why have I changed all these things?
A class with all public data isn't a class, but an aggregation of data. I'd use a struct for that, to not to confuse those who later need to maintain my code. (That might include me a few years down the road, which is a strong incentive to be very helpful.)
Unless you know exactly what you're doing (which doesn't seem to be the case), you should be using std::string rather than C-style strings.
Why pass the function parameter by pointer, when you can use a const reference?
std::ifstream has a constructor with which you can open a file immediately. I rarely ever use (or see used) its open() member function.
You need to test whether the file was opened and whether the input operations succeeded. (In this case, I merged the test whether it could be opened with the input operation succeed test, since the for loop is a pre-testing loop.) After the operations, I check whether an error occurred. Alternatively you could break out of the loop with an exception when reading fails.
Usually there is no need to close a stream, since its destructor already does this. If you make your variables as local as possible (which is good programming praxis), the file will be closed right away nevertheless.
Related
#include <iostream>
#include <map>
using namespace std;
string read(string a){return "abc";}
void read(float a){}
bool read(int a){return true;}
int main()
{
map<string,string(*)(string)> f1;
map<string,void(*)(float)> f2;
map<string,bool(*)(int)> f3;
f1["read"]=read;
f2["read"]=read;
f3["read"]=read;
string t,u;
while(1)
{
cin>>t>>u;
if(!f1.count(t)||!f2.count(t)||!f3.count(t)) cout<<"Unknown command!\n";
else cout<<f1[t](u);
}
}
Here I want to access these overloaded functions through their keys. But how can I (or can I ever) store them all in one single map? Something like map<string,/*---*/> f; that is capable of mapping different functions that have different parameter types and different return types so that I can use f["read"]=read; at once?
You cannot have a set of overloads as one element in a map. You could put some objects in the map that have overloaded member functions, though that also wont help, as you want elements in the map with different overloads.
Next problem is that when the parameters are from user input, you need to decide what overload you want to call before you call it. Usually you let the compiler decide based on paramters, but here you need to parse user input to the type of the parameters.
Further, elements in the map must all be of same type. That type should provide an interface that allows you to call the functions in a convenient way.
In other words, the easy way is to wrap functions into something that always takes the same paramter and always returns the same and then put that something into the map. I suggest to use std::istream for input and std::ostream for output :
#include <iostream>
#include <map>
#include <functional>
std::string read(std::string a){return "abc";}
void read(float a){}
bool read(int a){return true;}
int main()
{
std::map<std::string,std::function< void(std::istream&,std::ostream&)>> f;
f["read"] = [](std::istream& in,std::ostream& out){
std::string input;
in >> input;
// put logic to decide what overload to call here
bool call_string = true;
bool call_int = false;
bool call_bool = false;
if (call_string) {
out << read("foo");
} else if (call_int) {
out << read(42);
} else if (call_bool) {
//note : read(bool) returns void
read(false);
};
// use the map:
std::string t;
std::cin >> t;
f[t](std::cin,std::cout);
}
For input "read 42" the output is
abc
Live Example
PS: I you wouldn't insist on overloads the parsing could be automated to some extend by deducing the argument and return types of the functions to be put in the map, though it wont work with overloads (and would be a different question).
I handled the case according to #idclev 463035818's first suggestions, thanks to them.
Here's my recent sample;
#include <iostream>
#include <fstream>
#include <map>
using namespace std;
typedef string (*func)(string);
struct demo
{
map<string,func> f;
}g;
string read_p(string),read(string),read(int);
int main()
{
g.f["read"]=read_p;
while(1)
{
cout<<">>";
string t;
getline(cin,t);
size_t found=t.find(" ");
if(found==-1){if(!g.f.count(t)) cout<<"Unrecognized command!\n"; else cout<<g.f[t](t);}
else
{
string a=t.substr(0,found),b=t.substr(found+1,t.length()-found);
if(!g.f.count(a)) cout<<"Unrecognized long command!\n"; else cout<<g.f[a](b);
}
}
return 0;
}
string read_p(string c)
{
if(c=="read") return "Read what?\n";
else if(c=="story") return read(c);
else if(c.substr(0,4)!="page") return "Can't read that!\n";
else
{
size_t found=c.find_first_of("0123456789");
if(found==-1) return "Choose a page!\n";
else
{
int d=atoi((c.substr(found,2)).c_str());
return read(d);
}
}
}
string read(string a)
{
ifstream counter("game.txt",ifstream::in);
string x3;
int line=0;
while(counter.good()){getline(counter,x3); line++;}
counter.close();
string x1[line],x2="\"";
ifstream x("game.txt",ifstream::in);
for(int i=0; i<line; i++)
{
getline(x,x1[i]);
x1[i].erase(0,3);
x2.append(x1[i]+"\n");
}
x.close();
x2.erase(x2.length()-2,2);
x2.append("\"\n");
return x2;
}
string read(int a)
{
if(a<14||a>18) return "Choose another page!\n";
int page=-1;
ifstream x("game.txt",ifstream::in);
string x1;
while(a!=page){getline(x,x1); page=atoi((x1.substr(0,2)).c_str());}
x1.erase(0,3);
x1.insert(0,"\"");
x1.append("\"\n");
return x1;
}
I put the map into a struct so it's also accessible from local functions. Then when anything begins with "read" is entered, program calls read_p function with the user-input string as parameter. read_p parses that in my easy way and decides which variation of read to call. read is overloaded to be able to handle strings and integers separately. When a string is passed as parameter, read(string a) is called; and when an int is passed, read(int a) is called.
This method works precisely. Mapping the functions that are in the same format and that will parse input, then overloading different functions related to them. If any other user-input commands to be added, we'll just add g.f["command"]=command_p; that will parse it and overloaded variations of the function command. I'm sure there must be better ways, if anyone wants to help, I'll be glad about it.
Something similar to what you suggested is used by Qt framework for event handling. Said framework casts all slot function pointers to void* and is cast back before use. The code generation for that is done by meta-object compiler.
I suspect its a implementation-defined behavior that actually is viable on most of platforms as longs as you properly track which pointer points at which function. Lately their design became more complex to support lambda expressions for handlers.
class RF
{
public:
bitset<32> ReadData1, ReadData2;
RF()
{
Registers.resize(32);
Registers[0] = bitset<32> (0);
}
void ReadWrite(bitset<5> RdReg1, bitset<5> RdReg2, bitset<5> WrtReg, bitset<32> WrtData, bitset<1> WrtEnable)
{
// implement the funciton by you.
}
void OutputRF() // write RF results to file
{
ofstream rfout;
rfout.open("RFresult.txt",std::ios_base::app);
if (rfout.is_open())
{
rfout<<"A state of RF:"<<endl;
for (int j = 0; j<32; j++)
{
rfout << Registers[j]<<endl;
}
}
else cout<<"Unable to open file";
rfout.close();
}
private:
vector<bitset<32> >Registers;
};
RF() is the constructor, but since all it does is resize Registers to 32, you can remove it if you specify that initialization on the member directly, like this:
vector<bitset<32> > Registers = vector<bitset<32> >(32);
Then Registers will be constructed with size 32x32 bits by default, and all the bits will be zero as well, so you can remove the entire RF() function.
Note: At first I thought you could use vector<bitset<32> > Registers{32} but due to vagaries of C++ syntax that does the wrong thing. Thanks to Fureeish for that.
The short answer to your question is that, yes, for your current program, it is necessary.
The RF() function in this case is the function called when we initialize the RF object, eg.
RF new_RF;
Would run the RF() function and set things up. For this reason, it is called a 'constructor', because it helps you 'construct' your class.
In your case, the constructor is necessary for your program because it sets up your Registers variable, so that the code below from your OutputRF() function can run.
for (int j = 0; j<32; j++)
{
rfout << Registers[j]<<endl;
}
It's also useful because we can use it to set up many things, for example, if our RF() constructor looked like this:
RF(int a)
{
Registers.resize(a);
Registers[0] = bitset<a> (0);
}
It would instead resize the RF Registers to int a. You can look here for a more in-depth tutorial about constructors.
Hope that helps!
I have a class called Game which contains the following:
vector<shared_ptr<A>> attr; // attributes
D diff; // differences
vector<shared_ptr<C>> change; // change
My question is, how can I write these (save) to a file and read/load it up later?
I thought about using a struct with these in it, and simply saving the struct but I have no idea where to start.
This is my attempt so far, with just trying to save change. I've read up a lot on the issue and my issue (well one of them, anyway) here seems to be that I am storing pointers which after closing the program would be invalid (compounded by the fact that I also free them before exiting).
/* Saves state to file */
void Game::saveGame(string toFile) {
ofstream ofs(toFile, ios::binary);
ofs.write((char *)&this->change, sizeof(C));
/* Free memory code here */
....
exit(0);
};
/* Loads game state from file */
void Game::loadGame(string fromFile) {
ifstream ifs(fromFile, ios::binary);
ifs.read((char *)&this->change, sizeof(C));
this->change.toString(); // display load results
};
Can anyone guide me in the right direction for serializing this data? I'd like to use only standard packages, so no boost.
Thanks.
I have no idea how is implemented classes A, C or D, but that is the first question: how to serialize an object of that class. For the C case, you need to implement something like this:
std::ostream& operator <<(std::ostream& os, const C& c) {
// ... code to serialize c to an output stream
return os;
}
std::istream& operator >>(std::istream& is, C& c) {
// ... code to populate c contents from the input stream
return is;
}
or, if you prefer, create a write() and read() function for that class.
Well, if you want to serialize a vector<shared_ptr<C>> looks obvious you don't want to serialize the pointer, but the contents. So you need to dereference each of those pointers and serialize. If the size of the vector is not known before loading it (i.e., is not always the same), you'll need to store that information. Then, you can create a pair of functions to serialize the complete vector:
std::ostream& operator <<(std::ostream& os, const std::vector<std::shared_ptr<C>>& vc) {
// serialize the size of the vector using << operator
// for each element of the vector, let it be called 'pc'
os << *pc << std::endl; // store the element pointed by the pointer, not the pointer.
return os;
}
std::istream& operator >>(std::istream& is, std::vector<std::shared_ptr<C>>& c) {
// read the size of the vector using >> operator
// set the size of the vector
// for each i < sizeo of the vector, let 'auto &pc = vc[i]' be a reference to the i-th element of the vector
C c; // temporary object
is >> c; // read the object stored in the stream
pc = std::make_shared<C>(c); // construct the shared pointer, assuming the class C has copy constructor
return is;
}
And then,
/* Saves state to file */
void Game::saveGame(string toFile) {
ofstream ofs(toFile);
ofs << change;
....
};
/* Loads game state from file */
void Game::loadGame(string fromFile) {
ifstream ifs(fromFile);
ifs >> change;
};
I know there are a lot of things you still need to resolve. I suggest you to investigate to resolve them so you understand well how to solve your problem.
Not only are you saving pointers, you're trying to save a shared_ptr but using the wrong size.
You need to write serialization functions for all your classes, taking care to never just write the raw bits of a non-POD type. It's safest to always implement member-by-member serialization for everything, because you never know what the future will bring.
Then handling collections of them is just a matter of also storing how many there are.
Example for the Cs:
void Game::save(ofstream& stream, const C& data)
{
// Save data as appropriate...
}
void Game::saveGame(string toFile) {
ofstream ofs(toFile, ios::binary);
ofs.write((char *)change.size(), sizeof(change.size());
for (vector<shared_ptr<C>>::const_iterator c = change.begin(); c != change.end(); ++c)
{
save(ofs, **c);
}
};
shared_ptr<C> Game::loadC(ofstream& stream)
{
shared_ptr<C> data(new C);
// load the object...
return data;
}
void Game::loadGame(string fromFile) {
change.clear();
size_t count = 0;
ifstream ifs(fromFile, ios::binary);
ifs.read((char *)&count, sizeof(count));
change.reserve(count);
for (int i = 0; i < count; ++i)
{
change.push_back(loadC(ifs));
}
};
All the error handling is missing of course - you would need to add that.
It's actually a good idea to at least start with text storage (using << and >>) instead of binary. It's easier to find bugs, or mess around with the saved state, when you can just edit it in a text editor.
Writing your own serialization is quite a challenge. Even if you do not use boost serializatoin I would recommend you learn how to use it and comprehend how it works rather than discovering it yourself.
When serializing you finally end up with a buffer of data of which content you have very vague idea. You have to save everything you need to be able to restore it. You read it chunk by chunk. Example (not compiled, not tested and not stylish ):
void save(ostream& out, const string& s)
{
out << s.size();
out.write(s.c_str(), s.size());
}
void load(istream& in, string& s)
{
unsigned len;
in >> len;
s.resize(len);
in.read((char*)s, len);
}
struct Game
{
void save(ostream& out)
{
player.save(out);
};
void load(istream& in)
{
player.load(in);
}
};
struct Player
{
void save(ostream& out)
{
// save in the same order as loading, serializing everything you need to read it back
save(out, name);
save(out, experience);
}
void load(istream& in)
{
load(in, name);
load(in, experience); //
}
};
I do not know why you would do it to yourself instead of using boost but those are some of the cases you should consider:
- type - you must figure out a way to know what "type of change" you actually have there.
- a string (vector, whatever) - size + data (then the first thing you read back from the string is the length, you resize it and copy the "length" number of characters)
- a pointer - save the data pointed by pointer, then upon deserialization you have to allocate it, construct it (usually default construct) and read back the data and reset the members to their respective values. Note: you have to avoid memory leakage.
- polymorphic pointer - ouch you have to know what type the pointer actually points to, you have to construct the derived type, save the values of the derived type... so you have to save type information
- null pointer... you have to distinguish null pointer so you know that you do not need to further read data from the stream.
- versioning - you have to be able to read a data after you added/removed a field
There is too much of it for you to get a complete answer.
I'm trying to implement a minheap in C++. However the following code keeps eliciting errors such as :
heap.cpp:24:4: error: cannot convert 'complex int' to 'int' in assignment
l=2i;
^
heap.cpp:25:4: error: cannot convert 'complex int' to 'int' in assignment
r=2i+1;
^
heap.cpp: In member function 'int Heap::main()':
heap.cpp:47:16: error: no matching function for call to 'Heap::heapify(int [11], int&)'
heapify(a,i);
^
heap.cpp:47:16: note: candidate is:
heap.cpp:21:5: note: int Heap::heapify(int)
int heapify(int i) //i is the parent index, a[] is the heap array
^
heap.cpp:21:5: note: candidate expects 1 argument, 2 provided
make: * [heap] Error 1
#include <iostream>
using namespace std;
#define HEAPSIZE 10
class Heap
{
int a[HEAPSIZE+1];
Heap()
{
for (j=1;j<(HEAPISZE+1);j++)
{
cin>>a[j];
cout<<"\n";
}
}
int heapify(int i) //i is the parent index, a[] is the heap array
{
int l,r,smallest,temp;
l=2i;
r=2i+1;
if (l<11 && a[l]<a[i])
smallest=l;
else
smallest=i;
if (r<11 && a[r]<a[smallest])
smallest=r;
if (smallest != i)
{
temp = a[smallest];
a[smallest] = a[i];
a[i]=temp;
heapify(smallest);
}
}
int main()
{
int i;
for (i=1;i<=HEAPSIZE;i++)
{
heapify(a,i);
}
}
}
Ultimately, the problem with this code is that it was written by someone who skipped chapters 1, 2 and 3 of "C++ for Beginners". Lets start with some basics.
#include <iostream>
using namespace std;
#define HEAPSIZE 10
Here, we have included the C++ header for I/O (input output). A fine start. Then, we have issued a directive that says "Put everything that is in namespace std into the global namespace". This saves you some typing, but means that all of the thousands of things that were carefully compartmentalized into std:: can now conflict with names you want to use in your code. This is A Bad Thing(TM). Try to avoid doing it.
Then we went ahead and used a C-ism, a #define. There are times when you'll still need to do this in C++, but it's better to avoid it. We'll come back to this.
The next problem, at least in the code you posted, is a misunderstanding of the C++ class.
The 'C' language that C++ is based on has the concept of a struct for describing a collection of data items.
struct
{
int id;
char name[64];
double wage;
};
It's important to notice the syntax - the trailing ';'. This is because you can describe a struct and declare variables of it's type at the same time.
struct { int id; char name[64]; } earner, manager, ceo;
This declares a struct, which has no type name, and variables earner, manager and ceo of that type. The semicolon tells the compiler when we're done with this statement. Learning when you need a semicolon after a '}' takes a little while; usually you don't, but in struct/class definition you do.
C++ added lots of things to C, but one common misunderstanding is that struct and class are somehow radically different.
C++ originally extended the struct concept by allowing you to describe functions in the context of the struct and by allowing you to describe members/functions as private, protected or public, and allowing inheritance.
When you declare a struct, it defaults to public. A class is nothing more than a struct which starts out `private.
struct
{
int id;
char name[64];
double wage;
};
class
{
public:
int id;
char name[64];
double wage;
};
The resulting definitions are both identical.
Your code does not have an access specifier, so everything in your Heap class is private. The first and most problematic issue this causes is: Nobody can call ANY of your functions, because they are private, they can only be called from other class members. That includes the constructor.
class Foo { Foo () {} };
int main()
{
Foo f;
return 0;
}
The above code will fail to compile, because main is not a member of Foo and thus cannot call anything private.
This brings us to another problem. In your code, as posted, main is a member of Foo. The entry point of a C++ program is main, not Foo::main or std::main or Foo::bar::herp::main. Just, good old int main(int argc, const char* argv[]) or int main().
In C, with structs, because C doesn't have member functions, you would never be in a case where you were using struct-members directly without prefixing that with a pointer or member reference, e.g. foo.id or ptr->wage. In C++, in a member function, member variables can be referenced just like local function variables or parameters. This can lead to some confusion:
class Foo
{
int a, b;
public:
void Set(int a, int b)
{
a = a; // Erh,
b = b; // wat???
}
};
There are many ways to work around this, but one of the most common is to prefix member variables with m_.
Your code runs afoul of this, apparently the original in C passed the array to heapify, and the array was in a local variable a. When you made a into a member, leaving the variable name exactly the same allowed you not to miss the fact that you no-longer need to pass it to the object (and indeed, your heapify member function no-longer takes an array as a pointer, leading to one of your compile errors).
The next problem we encounter, not directly part of your problem yet, is your function Heap(). Firstly, it is private - you used class and haven't said public yet. But secondly, you have missed the significance of this function.
In C++ every struct/class has an implied function of the same name as the definition. For class Heap that would be Heap(). This is the 'default constructor'. This is the function that will be executed any time someone creates an instance of Heap without any parameters.
That means it's going to be invoked when the compiler creates a short-term temporary Heap, or when you create a vector of Heap()s and allocate a new temporary.
These functions have one purpose: To prepare the storage the object occupies for usage. You should try and avoid as much other work as possible until later. Using std::cin to populate members in a constructor is one of the most awful things you can do.
We now have a basis to begin to write the outer-shell of the code in a fashion that will work.
The last change is the replacement of "HEAPSIZE" with a class enum. This is part of encapsulation. You could leave HEAPSIZE as a #define but you should expose it within your class so that external code doesn't have to rely on it but can instead say things like Heap::Size or heapInstance.size() etc.
#include <iostream>
#include <cstdint> // for size_t etc
#include <array> // C++11 encapsulation for arrays.
struct Heap // Because we want to start 'public' not 'private'.
{
enum { Size = 10 };
private:
std::array<int, Size> m_array; // meaningful names ftw.
public:
Heap() // default constructor, do as little as possible.
: m_array() // says 'call m_array()s default ctor'
{}
// Function to load values from an istream into this heap.
void read(std::istream& in)
{
for (size_t i = 0; i < Size; ++i)
{
in >> m_array[i];
}
return in;
}
void write(std::ostream& out)
{
for (size_t i = 0; i < Size; ++i)
{
if (i > 0)
out << ','; // separator
out << m_array[i];
}
}
int heapify(size_t index)
{
// implement your code here.
}
}; // <-- important.
int main(int argc, const char* argv[])
{
Heap myHeap; // << constructed but not populated.
myHeap.load(std::cin); // read from cin
for (size_t i = 1; i < myHeap.Size; ++i)
{
myHeap.heapify(i);
}
myHead.write(std::cout);
return 0;
}
Lastly, we run into a simple, fundamental problem with your code. C++ does not have implicit multiplication. 2i is the number 2 with a suffix. It is not the same as 2 * i.
int l = 2 * i;
There is also a peculiarity with your code that suggests you are mixing between 0-based and 1-based implementation. Pick one and stick with it.
--- EDIT ---
Technically, this:
myHeap.load(std::cin); // read from cin
for (size_t i = 1; i < myHeap.Size; ++i)
{
myHeap.heapify(i);
}
is poor encapsulation. I wrote it this way to draw on the original code layout, but I want to point out that one reason for separating construction and initialization is that it allows initialization to be assured that everything is ready to go.
So, it would be more correct to move the heapify calls into the load function. After all, what better time to heapify than as we add new values, keeping the list in order the entire time.
for (size_t i = 0; i < Size; ++i)
{
in >> m_array[i];
heapify(i);
}
Now you've simplified your classes api, and users don't have to be aware of the internal machinery.
Heap myHeap;
myHeap.load(std::cin);
myHeap.write(std::cout);
Let me set the scene..
You can open files in a specific mode like this:
#include <fstream>
int main(){
std::fstream myfile;
myfile.open ("filename", std::ios::app);
return 0;
}
that second parameter is an enumerated type-
which is why you will get a compiler error attempting this:
#include <fstream>
int main(){
std::fstream myfile;
myfile.open ("filename", std::ios::lksdjflskdjflksff);
return 0;
}
In this example, the class doesn't have to account for the second parameter being incorrect, and the programmer never has to worry about passing in a nonsensical value.
Question: Is there a way to write functions that must take a particular type AND a particular value?
Let's say I wanted to re-implement a File Handling class similar to the one above.
The difference is I'm making the second parameter a char instead of an enumerated type.
How could I get something like this to work:
#include "MyFileHandler.h"
int main(){
MyFileHandler myfile1;
myfile.open ("filename", 'a'); //GOOD: a stands for append
myfile.open ("filename", 't'); //GOOD: t stands for truncate
myfile.open ("filename", 'x'); //COMPILER ERROR: openmode can not be the value 'x'
return 0;
}
Going beyond this, can I get the compiler to test the validity of argument values through functional means?
Example:
void IOnlyAcceptPrimeNumbers(const int & primeNumber);
int function(void);
int main(){
IOnlyAcceptPrimeNumbers(3); //GOOD: 3 is prime
IOnlyAcceptPrimeNumbers(7); //GOOD: 7 is prime
IOnlyAcceptPrimeNumbers(10); //COMPILER ERROR: 10 is not prime
IOnlyAcceptPrimeNumbers(10+1); //GOOD: 11 is prime
IOnlyAcceptPrimeNumbers(1+1+1+1); //COMPILER ERROR: 4 is not prime
IOnlyAcceptPrimeNumbers(function()); //GOOD: can this somehow be done?
return 0;
}
void IOnlyAcceptPrimeNumbers(const int & primeNumber){return;}
int function(void){return 7;}
I believe i've made it clear what I want to do and why I find it important.
Any solutions out there?
If you want compile-time checked values, you could write templates rather than function arguments:
template <char> void foo(std::string const &); // no implementation
template <> void foo<'a'>(std::string const & s) { /* ... */ }
template <> void foo<'b'>(std::string const & s) { /* ... */ }
Usage:
foo<'a'>("hello world"); // OK
foo<'z'>("dlrow olleh"); // Linker error, `foo<'z'>` not defined.
If you want an actual compiler error rather than just a linker error, you could add a static_assert(false) into the primary template.
No, if you specify that your function will take a char, it will take any char.
The "resolution" used by the compiler for checking passed arguments is the type rather than a set of possible values.
In other words, you need to use enumerations for this, or move the checking to runtime, or do something horrid like:
static void processAorT (char typ, char *fileName) { ... }
void processA (char *fileName) { processAorT ('a', fileName); }
void processT (char *fileName) { processAorT ('t', fileName); |
(not something I would advise, by the way).
Having said that, I'm not sure what you're proposing is a good idea anyway.
The compiler may be able to detect invalid constants, but won't be very successful if the parameter passed into IOnlyAcceptPrimeNumbers has come from a variable or, worse, input by a user.
The API is a contract between caller and function and, if the rules of that contract are not followed, you're free to do whatever you want, though hopefully you'd document it.
In other words, that function should begin:
void IOnlyAcceptPrimeNumbers (int num) {
if (!isPrime (num)) return;
// do something with a prime number.
}
(or the equivalent for your function that accepts a and t but not x). Doing nothing when passed invalid parameters is a reasonable strategy, as is returning an error or throwing an exception (though no doubt some would argue with this).
How you handle it is up to you, but it needs to be handled at runtime simply because the compiler doesn't have all the information.
You can only check value validity at runtime. Best you can do is use assert to stop programm execution if precondition is violated.
No. If you want to restrict the accepted arguments you need to use enums or accept an object that inherits from a specific interface (depends how sophisticated you want to make it). Enums is the common way to address this issue.
The example about the IOnlyAcceptPrimeNumbers is not well designed. If you want to achieve something similar it would be better to provide a class method that is something such as bool setNumber(int number) that will return false if the number is not prime. If you want to do it in the costructor the real alternative is to throw an exception (that is not really nice to do).
The concept is that you can not simply rely that the user will pass you only elements from a (correct) subset of the values that the parameter type allows.
While more restrictive than your requirements (this limits the values a particular type can hold), you can always try something like:
// Vowel.h
#ifndef VOWEL_H_
#define VOWEL_H_
class Vowel
{
public:
static const Vowel A;
static const Vowel E;
static const Vowel I;
static const Vowel O;
static const Vowel U;
char get() const { return value; }
private:
explicit Vowel(char c);
char value;
};
#endif /* VOWEL_H_ */
// Vowel.cpp
#include "Vowel.h"
Vowel::Vowel(char c) : value(c) {}
const Vowel Vowel::A('A');
const Vowel Vowel::E('E');
const Vowel Vowel::I('I');
const Vowel Vowel::O('O');
const Vowel Vowel::U('U');
Since the char constructor is private, only Vowel itself can construct objects from chars. All other uses are done by copy construction or copy assignment.
(I think I originally learned this technique from Scott Meyers; thank him / blame me.)