lab 4 complete
This commit is contained in:
parent
2769a3a0ad
commit
69af73e315
12 changed files with 476 additions and 79 deletions
59
lab4/.vscode/settings.json
vendored
Normal file
59
lab4/.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
{
|
||||||
|
"files.associations": {
|
||||||
|
"array": "cpp",
|
||||||
|
"atomic": "cpp",
|
||||||
|
"bit": "cpp",
|
||||||
|
"*.tcc": "cpp",
|
||||||
|
"bitset": "cpp",
|
||||||
|
"cctype": "cpp",
|
||||||
|
"charconv": "cpp",
|
||||||
|
"clocale": "cpp",
|
||||||
|
"cmath": "cpp",
|
||||||
|
"compare": "cpp",
|
||||||
|
"concepts": "cpp",
|
||||||
|
"cstdarg": "cpp",
|
||||||
|
"cstddef": "cpp",
|
||||||
|
"cstdint": "cpp",
|
||||||
|
"cstdio": "cpp",
|
||||||
|
"cstdlib": "cpp",
|
||||||
|
"ctime": "cpp",
|
||||||
|
"cwchar": "cpp",
|
||||||
|
"cwctype": "cpp",
|
||||||
|
"deque": "cpp",
|
||||||
|
"string": "cpp",
|
||||||
|
"unordered_map": "cpp",
|
||||||
|
"vector": "cpp",
|
||||||
|
"exception": "cpp",
|
||||||
|
"algorithm": "cpp",
|
||||||
|
"functional": "cpp",
|
||||||
|
"iterator": "cpp",
|
||||||
|
"memory": "cpp",
|
||||||
|
"memory_resource": "cpp",
|
||||||
|
"numeric": "cpp",
|
||||||
|
"optional": "cpp",
|
||||||
|
"random": "cpp",
|
||||||
|
"regex": "cpp",
|
||||||
|
"string_view": "cpp",
|
||||||
|
"system_error": "cpp",
|
||||||
|
"tuple": "cpp",
|
||||||
|
"type_traits": "cpp",
|
||||||
|
"utility": "cpp",
|
||||||
|
"format": "cpp",
|
||||||
|
"initializer_list": "cpp",
|
||||||
|
"iomanip": "cpp",
|
||||||
|
"iosfwd": "cpp",
|
||||||
|
"iostream": "cpp",
|
||||||
|
"istream": "cpp",
|
||||||
|
"limits": "cpp",
|
||||||
|
"new": "cpp",
|
||||||
|
"numbers": "cpp",
|
||||||
|
"ostream": "cpp",
|
||||||
|
"span": "cpp",
|
||||||
|
"sstream": "cpp",
|
||||||
|
"stdexcept": "cpp",
|
||||||
|
"streambuf": "cpp",
|
||||||
|
"text_encoding": "cpp",
|
||||||
|
"typeinfo": "cpp",
|
||||||
|
"variant": "cpp"
|
||||||
|
}
|
||||||
|
}
|
74
lab4/Sieve.cc
Normal file
74
lab4/Sieve.cc
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class Sieve {
|
||||||
|
private:
|
||||||
|
std::string sieve;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Constructor to initialize sieve with 'P' (prime assumption)
|
||||||
|
Sieve(size_t limit) : sieve(limit + 1, 'P') {
|
||||||
|
// 0 and 1 are not primes
|
||||||
|
sieve[0] = 'C';
|
||||||
|
if (limit > 0) sieve[1] = 'C';
|
||||||
|
|
||||||
|
// Sieve of Eratosthenes
|
||||||
|
for (size_t i = 2; i * i <= limit; ++i) {
|
||||||
|
if (sieve[i] == 'P') {
|
||||||
|
for (size_t j = i * i; j <= limit; j += i) {
|
||||||
|
sieve[j] = 'C';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get primes as a vector of integers
|
||||||
|
std::vector<int> getPrimes() const {
|
||||||
|
std::vector<int> primes;
|
||||||
|
for (size_t i = 2; i < sieve.size(); ++i) {
|
||||||
|
if (sieve[i] == 'P') {
|
||||||
|
primes.push_back(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return primes;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print all primes in the range
|
||||||
|
void printPrimesInRange(int start, int end) const {
|
||||||
|
for (int i = start; i <= end; ++i) {
|
||||||
|
if (sieve[i] == 'P') {
|
||||||
|
std::cout << i << " ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::cout << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the largest prime below a limit
|
||||||
|
int largestPrimeBelow(int limit) const {
|
||||||
|
for (int i = limit; i >= 2; --i) {
|
||||||
|
if (sieve[i] == 'P') {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1; // If no prime is found
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Main function for testing
|
||||||
|
int main() {
|
||||||
|
const int limit = 100000;
|
||||||
|
|
||||||
|
// Create a Sieve instance for the range 0 to limit
|
||||||
|
Sieve sieve(limit);
|
||||||
|
|
||||||
|
// Print primes between 1 and 200
|
||||||
|
std::cout << "Primes between 1 and 200:" << std::endl;
|
||||||
|
sieve.printPrimesInRange(1, 200);
|
||||||
|
|
||||||
|
// Find and print the largest prime below 100,000
|
||||||
|
int largestPrime = sieve.largestPrimeBelow(limit);
|
||||||
|
std::cout << "Largest prime below " << limit << ": " << largestPrime << std::endl;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
32
lab4/TagRemover.cc
Normal file
32
lab4/TagRemover.cc
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
#include <iostream>
|
||||||
|
#include <regex>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class TagRemover {
|
||||||
|
private:
|
||||||
|
std::string content;
|
||||||
|
|
||||||
|
public:
|
||||||
|
TagRemover(std::istream& input) {
|
||||||
|
std::string line;
|
||||||
|
while (std::getline(input, line)) {
|
||||||
|
content += line + '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void print(std::ostream& output) const {
|
||||||
|
std::string result = std::regex_replace(content, std::regex("<[^>]*>"), "");
|
||||||
|
result = std::regex_replace(result, std::regex("<"), "<");
|
||||||
|
result = std::regex_replace(result, std::regex(">"), ">");
|
||||||
|
result = std::regex_replace(result, std::regex(" "), " ");
|
||||||
|
result = std::regex_replace(result, std::regex("&"), "&");
|
||||||
|
|
||||||
|
output << result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
TagRemover tr(std::cin);
|
||||||
|
tr.print(std::cout);
|
||||||
|
return 0;
|
||||||
|
}
|
76
lab4/date.cc
76
lab4/date.cc
|
@ -1,30 +1,86 @@
|
||||||
#include <ctime> // time and localtime
|
#include <ctime> // för tid och localtime
|
||||||
|
#include <iomanip> // för setw och setfill
|
||||||
|
#include <sstream> // för inputhantering
|
||||||
#include "date.h"
|
#include "date.h"
|
||||||
|
|
||||||
int Date::daysPerMonth[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
int Date::daysPerMonth[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
||||||
|
|
||||||
|
// Konstruktor: dagens datum
|
||||||
Date::Date() {
|
Date::Date() {
|
||||||
time_t timer = time(0); // time in seconds since 1970-01-01
|
time_t timer = time(0); // tid i sekunder sedan 1970-01-01
|
||||||
tm* locTime = localtime(&timer); // broken-down time
|
tm* locTime = localtime(&timer); // lokal tid
|
||||||
year = 1900 + locTime->tm_year;
|
year = 1900 + locTime->tm_year;
|
||||||
month = 1 + locTime->tm_mon;
|
month = 1 + locTime->tm_mon;
|
||||||
day = locTime->tm_mday;
|
day = locTime->tm_mday;
|
||||||
}
|
}
|
||||||
|
|
||||||
Date::Date(int y, int m, int d) {}
|
// Konstruktor: specifikt datum
|
||||||
|
Date::Date(int y, int m, int d) : year(y), month(m), day(d) {}
|
||||||
|
|
||||||
|
// Get-funktioner
|
||||||
int Date::getYear() const {
|
int Date::getYear() const {
|
||||||
return 0;
|
return year;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Date::getMonth() const {
|
int Date::getMonth() const {
|
||||||
return 0;
|
return month;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Date::getDay() const {
|
int Date::getDay() const {
|
||||||
return 0;
|
return day;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Gå till nästa dag
|
||||||
void Date::next() {
|
void Date::next() {
|
||||||
|
day++;
|
||||||
|
if (day > daysPerMonth[month - 1] + (month == 2 && isLeapYear(year))) {
|
||||||
|
day = 1;
|
||||||
|
month++;
|
||||||
|
if (month > 12) {
|
||||||
|
month = 1;
|
||||||
|
year++;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Kontrollera om ett år är ett skottår
|
||||||
|
bool Date::isLeapYear(int year) {
|
||||||
|
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overloaded operator<< (output)
|
||||||
|
std::ostream& operator<<(std::ostream& os, const Date& date) {
|
||||||
|
os << std::setw(4) << std::setfill('0') << date.getYear() << '-'
|
||||||
|
<< std::setw(2) << std::setfill('0') << date.getMonth() << '-'
|
||||||
|
<< std::setw(2) << std::setfill('0') << date.getDay();
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overloaded operator>> (input)
|
||||||
|
std::istream& operator>>(std::istream& is, Date& date) {
|
||||||
|
std::string input;
|
||||||
|
is >> input;
|
||||||
|
|
||||||
|
std::istringstream iss(input);
|
||||||
|
char dash1, dash2;
|
||||||
|
int y, m, d;
|
||||||
|
|
||||||
|
if (iss >> y >> dash1 >> m >> dash2 >> d && dash1 == '-' && dash2 == '-') {
|
||||||
|
// Validera månad och dag
|
||||||
|
if (m >= 1 && m <= 12) {
|
||||||
|
int maxDay = Date::daysPerMonth[m - 1];
|
||||||
|
if (m == 2 && Date::isLeapYear(y)) {
|
||||||
|
maxDay = 29; // Februari har 29 dagar under skottår
|
||||||
|
}
|
||||||
|
|
||||||
|
if (d >= 1 && d <= maxDay) {
|
||||||
|
date = Date(y, m, d); // Sätt datumet om det är giltigt
|
||||||
|
return is;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ogiltig inmatning
|
||||||
|
is.setstate(std::ios_base::failbit);
|
||||||
|
return is;
|
||||||
|
}
|
||||||
|
|
28
lab4/date.h
28
lab4/date.h
|
@ -1,19 +1,27 @@
|
||||||
#ifndef DATE_H
|
#ifndef DATE_H
|
||||||
#define DATE_H
|
#define DATE_H
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
class Date {
|
class Date {
|
||||||
public:
|
public:
|
||||||
Date(); // today's date
|
Date(); // dagens datum
|
||||||
Date(int y, int m, int d); // yyyy-mm-dd
|
Date(int y, int m, int d); // yyyy-mm-dd
|
||||||
int getYear() const; // get the year
|
int getYear() const; // returnerar året
|
||||||
int getMonth() const; // get the month
|
int getMonth() const; // returnerar månaden
|
||||||
int getDay() const; // get the day
|
int getDay() const; // returnerar dagen
|
||||||
void next(); // advance to next day
|
void next(); // går till nästa dag
|
||||||
|
|
||||||
|
// Overloaded operators
|
||||||
|
friend std::ostream& operator<<(std::ostream& os, const Date& date);
|
||||||
|
friend std::istream& operator>>(std::istream& is, Date& date);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int year; // the year (four digits)
|
int year; // året (fyra siffror)
|
||||||
int month; // the month (1-12)
|
int month; // månaden (1-12)
|
||||||
int day; // the day (1-..)
|
int day; // dagen (1-...)
|
||||||
static int daysPerMonth[12]; // number of days in each month
|
static int daysPerMonth[12]; // antal dagar i varje månad
|
||||||
|
static bool isLeapYear(int year); // kontrollera skottår
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,64 +1,40 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <iomanip> // for setw and setfill
|
|
||||||
#include "date.h"
|
#include "date.h"
|
||||||
|
|
||||||
using std::cout;
|
|
||||||
using std::endl;
|
|
||||||
using std::setw;
|
|
||||||
using std::setfill;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Prints the date d in the format yyyy-mm-dd. You shall replace this
|
|
||||||
* function with an overloaded operator<<, and add an overloaded operator>>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
void print(const Date& d) {
|
|
||||||
cout << setw(4) << setfill('0') << d.getYear() << '-';
|
|
||||||
cout << setw(2) << setfill('0') << d.getMonth() << '-';
|
|
||||||
cout << setw(2) << setfill('0') << d.getDay();
|
|
||||||
}
|
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
// Check input and output of dates. Uncomment the following when you
|
// Test input och output
|
||||||
// have added operator>> and operator<<.
|
bool cont = true;
|
||||||
/*
|
while (cont) {
|
||||||
bool cont = true;
|
std::cout << "Type a date: ";
|
||||||
while (cont) {
|
Date aDate;
|
||||||
cout << "Type a date: ";
|
std::cin >> aDate;
|
||||||
Date aDate;
|
|
||||||
cin >> aDate;
|
if (std::cin.eof()) {
|
||||||
if (cin.eof()) {
|
cont = false;
|
||||||
cont = false;
|
} else if (!std::cin.good()) {
|
||||||
} else if (!cin.good()) {
|
std::cout << "Wrong input format" << std::endl;
|
||||||
cout << "Wrong input format" << endl;
|
std::cin.clear();
|
||||||
// restore stream state and ignore the rest of the line
|
std::cin.ignore(10000, '\n');
|
||||||
cin.clear();
|
} else {
|
||||||
cin.ignore(10000, '\n');
|
std::cout << "Output: " << aDate << std::endl;
|
||||||
}
|
}
|
||||||
else {
|
}
|
||||||
cout << "Output: " << aDate << endl;
|
|
||||||
}
|
// Testa 'next' med dagens datum
|
||||||
}
|
std::cout << "--- Today and more than a month ahead:" << std::endl;
|
||||||
*/
|
Date d1;
|
||||||
|
std::cout << d1 << std::endl;
|
||||||
// Check 'next' by creating an object describing today's date, then
|
for (int i = 1; i <= 35; ++i) {
|
||||||
// printing dates more than a month ahead
|
d1.next();
|
||||||
cout << "--- Today and more than a month ahead:" << endl;
|
std::cout << d1 << std::endl;
|
||||||
Date d1;
|
}
|
||||||
print(d1);
|
|
||||||
cout << endl;
|
// Testa 'next' från nyårsafton
|
||||||
for (int i = 1; i <= 35 ; ++i) {
|
std::cout << "--- New Year's Eve and the next day:" << std::endl;
|
||||||
d1.next();
|
Date d2(2013, 12, 31);
|
||||||
print(d1);
|
std::cout << d2 << std::endl;
|
||||||
cout << endl;
|
d2.next();
|
||||||
}
|
std::cout << d2 << std::endl;
|
||||||
|
|
||||||
// Check so 'next' functions correctly from one year to the next
|
return 0;
|
||||||
cout << "--- New Year's Eve and the next day:" << endl;
|
|
||||||
Date d2(2013, 12, 31);
|
|
||||||
print(d2);
|
|
||||||
cout << endl;
|
|
||||||
d2.next();
|
|
||||||
print(d2);
|
|
||||||
cout << endl;
|
|
||||||
}
|
}
|
||||||
|
|
56
lab4/makefile
Normal file
56
lab4/makefile
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
# Compiler
|
||||||
|
CXX = g++
|
||||||
|
CXXFLAGS = -std=c++17 -Wall -Wextra -pedantic
|
||||||
|
|
||||||
|
# Targets
|
||||||
|
TARGETS = TagRemover Sieve date_test toString_test string_cast_test
|
||||||
|
|
||||||
|
# Source files
|
||||||
|
SRCS_TAGREMOVER = TagRemover.cc
|
||||||
|
SRCS_SIEVE = Sieve.cc
|
||||||
|
SRCS_DATE = date.cc
|
||||||
|
SRCS_DATE_TEST = date_test.cc
|
||||||
|
SRCS_TOSTRING_TEST = toString_test.cc date.cc
|
||||||
|
SRCS_STRING_CAST_TEST = string_cast_test.cc date.cc
|
||||||
|
|
||||||
|
# Object files
|
||||||
|
OBJS_TAGREMOVER = $(SRCS_TAGREMOVER:.cc=.o)
|
||||||
|
OBJS_SIEVE = $(SRCS_SIEVE:.cc=.o)
|
||||||
|
OBJS_DATE = $(SRCS_DATE:.cc=.o)
|
||||||
|
OBJS_DATE_TEST = $(SRCS_DATE_TEST:.cc=.o)
|
||||||
|
OBJS_TOSTRING_TEST = $(SRCS_TOSTRING_TEST:.cc=.o)
|
||||||
|
OBJS_STRING_CAST_TEST = $(SRCS_STRING_CAST_TEST:.cc=.o)
|
||||||
|
|
||||||
|
# Default target
|
||||||
|
all: $(TARGETS)
|
||||||
|
|
||||||
|
# Rule to build TagRemover
|
||||||
|
TagRemover: $(OBJS_TAGREMOVER)
|
||||||
|
$(CXX) $(CXXFLAGS) -o $@ $^
|
||||||
|
|
||||||
|
# Rule to build Sieve
|
||||||
|
Sieve: $(OBJS_SIEVE)
|
||||||
|
$(CXX) $(CXXFLAGS) -o $@ $^
|
||||||
|
|
||||||
|
# Rule to build date_test
|
||||||
|
date_test: $(OBJS_DATE_TEST) $(OBJS_DATE)
|
||||||
|
$(CXX) $(CXXFLAGS) -o $@ $^
|
||||||
|
|
||||||
|
# Rule to build toString_test
|
||||||
|
toString_test: $(OBJS_TOSTRING_TEST)
|
||||||
|
$(CXX) $(CXXFLAGS) -o $@ $^
|
||||||
|
|
||||||
|
# Rule to build string_cast_test
|
||||||
|
string_cast_test: $(OBJS_STRING_CAST_TEST)
|
||||||
|
$(CXX) $(CXXFLAGS) -o $@ $^
|
||||||
|
|
||||||
|
# Rule to compile source files into object files
|
||||||
|
%.o: %.cc
|
||||||
|
$(CXX) $(CXXFLAGS) -c $<
|
||||||
|
|
||||||
|
# Clean up build artifacts
|
||||||
|
clean:
|
||||||
|
rm -f $(OBJS_TAGREMOVER) $(OBJS_SIEVE) $(OBJS_DATE) $(OBJS_DATE_TEST) $(OBJS_TOSTRING_TEST) $(OBJS_STRING_CAST_TEST) $(TARGETS)
|
||||||
|
|
||||||
|
# Phony targets
|
||||||
|
.PHONY: all clean
|
61
lab4/reflektion.txt
Normal file
61
lab4/reflektion.txt
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
1. In your tests, how did you test the error handling (e.g., that a wrong string_cast actually
|
||||||
|
throws?)
|
||||||
|
|
||||||
|
Genom att använda en ogiltig inmatning, som "abc" eller "123abc", och verifiera att std::invalid_argument kastas korrekt.
|
||||||
|
|
||||||
|
|
||||||
|
2. In TagRemover, why do you think the constructor takes an istream instead of just the
|
||||||
|
filename?
|
||||||
|
|
||||||
|
Det gör klassen mer flexibel eftersom den kan arbeta med vilken ström som helst, inte bara filer. Till exempel kan den användas med std::cin eller std::stringstream.
|
||||||
|
|
||||||
|
|
||||||
|
3. In TagRemover, did you process the file line by line, or did you first read the entire file?
|
||||||
|
What are the pros and cons of these two approaches?
|
||||||
|
|
||||||
|
Jag läste hela filen först eftersom det är enklare att använda regex för att bearbeta hela texten på en gång.
|
||||||
|
Fördelen är att det är mer effektivt för regex-matchning, men nackdelen är att det kräver mer minne för stora filer.
|
||||||
|
|
||||||
|
|
||||||
|
4. How do you read the entire contents of an std::istream into a std::string without using
|
||||||
|
a for or while loop?
|
||||||
|
|
||||||
|
Genom att använda:
|
||||||
|
std::string content((std::istreambuf_iterator<char>(istream)), std::istreambuf_iterator<char>());
|
||||||
|
|
||||||
|
|
||||||
|
5. In TagRemover, do you have duplicate code for translating the special characters? If so, how
|
||||||
|
would you refactor your code to avoid duplicate code?
|
||||||
|
|
||||||
|
Ja, det finns duplicerad kod för varje specialtecken. Jag skulle använda en std::map för att lagra mönster och ersättningar och iterera genom den:
|
||||||
|
std::map<std::string, std::string> replacements = {
|
||||||
|
{"<", "<"}, {">", ">"}, {" ", " "}, {"&", "&"}
|
||||||
|
};
|
||||||
|
for (const auto& [pattern, replacement] : replacements) {
|
||||||
|
result = std::regex_replace(result, std::regex(pattern), replacement);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
6. How do you check if an input or output operation on a stream (e.g., operator>> or
|
||||||
|
operator<<) has failed?
|
||||||
|
|
||||||
|
Genom att använda std::istream::fail() eller std::ostream::fail().
|
||||||
|
|
||||||
|
|
||||||
|
7. How do you know if you have reached the end of an istream?
|
||||||
|
|
||||||
|
Genom att använda std::istream::eof().
|
||||||
|
|
||||||
|
|
||||||
|
8. Does string_cast<int>("123kalle") return the value 123 or does it throw an exception?
|
||||||
|
How do you implement each of those behaviours?
|
||||||
|
|
||||||
|
Det kastar ett undantag eftersom std::istringstream misslyckas med att konsumera hela strängen. För att tillåta delvis inläsning skulle vi behöva anpassa funktionen.
|
||||||
|
|
||||||
|
|
||||||
|
9. When calling the function template toString, the template type argument is not ex-
|
||||||
|
plicitly given in the call. For string_cast, on the other hand, you have to specify
|
||||||
|
string_cast<int> or string_cast<Date>. What is the difference? When should explicit
|
||||||
|
template arguments be given to function templates?
|
||||||
|
|
||||||
|
toString använder <<-operatören, som automatiskt identifierar typen av objekt, medan string_cast kräver en explicit typ eftersom det behöver veta vad strängen ska konverteras till.
|
14
lab4/string_cast.h
Normal file
14
lab4/string_cast.h
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <stdexcept> // För std::invalid_argument
|
||||||
|
|
||||||
|
// Template function for string_cast
|
||||||
|
template <typename T>
|
||||||
|
T string_cast(const std::string& str) {
|
||||||
|
std::istringstream iss(str);
|
||||||
|
T value;
|
||||||
|
if (!(iss >> value) || !(iss.eof())) {
|
||||||
|
throw std::invalid_argument("Invalid conversion from string: " + str);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
29
lab4/string_cast_test.cc
Normal file
29
lab4/string_cast_test.cc
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include "date.h" // För Date-klassen
|
||||||
|
#include "string_cast.h" // Inkludera string_cast
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
try {
|
||||||
|
// Testa string_cast med int
|
||||||
|
int i = string_cast<int>("123");
|
||||||
|
std::cout << "Integer: " << i << std::endl;
|
||||||
|
|
||||||
|
// Testa string_cast med double
|
||||||
|
double d = string_cast<double>("12.34");
|
||||||
|
std::cout << "Double: " << d << std::endl;
|
||||||
|
|
||||||
|
// Testa string_cast med Date
|
||||||
|
Date date = string_cast<Date>("2015-01-10");
|
||||||
|
std::cout << "Date: " << date << std::endl;
|
||||||
|
|
||||||
|
// Testa ogiltig konvertering
|
||||||
|
int invalid = string_cast<int>("abc");
|
||||||
|
std::cout << "Invalid conversion: " << invalid << std::endl;
|
||||||
|
|
||||||
|
} catch (const std::invalid_argument& e) {
|
||||||
|
std::cout << "Error: " << e.what() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
10
lab4/toString.h
Normal file
10
lab4/toString.h
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
// Template function to convert an object to a string
|
||||||
|
template <typename T>
|
||||||
|
std::string toString(const T& obj) {
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << obj;
|
||||||
|
return oss.str();
|
||||||
|
}
|
22
lab4/toString_test.cc
Normal file
22
lab4/toString_test.cc
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
#include <iostream>
|
||||||
|
#include "date.h" // Inkludera Date-klassen från tidigare lösning
|
||||||
|
#include "toString.h" // Inkludera toString-mallen
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
// Testa med ett primitivt datatyper
|
||||||
|
double d = 1.234;
|
||||||
|
int i = 42;
|
||||||
|
std::string strDouble = toString(d);
|
||||||
|
std::string strInt = toString(i);
|
||||||
|
|
||||||
|
std::cout << "Double as string: " << strDouble << std::endl;
|
||||||
|
std::cout << "Integer as string: " << strInt << std::endl;
|
||||||
|
|
||||||
|
// Testa med Date-klassen
|
||||||
|
Date today(2023, 12, 11); // Skapa ett datumobjekt
|
||||||
|
std::string strDate = toString(today);
|
||||||
|
|
||||||
|
std::cout << "Date as string: " << strDate << std::endl;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in a new issue