/**
 * Michał Skrzędziejewski K1-ISIII2
 * Szablon funkcji lexical_cast służy do bezpiecznej i eleganckiej konwersji
 * wartości w postaci ciągu znaków na zmienne typu int, float, bool itp. Jest to mechanizm
 * zastępujący funkcje atox() z języka c. Istnieje też możliwość konwersji typów liczbowych
 * lub boolowskich na ciągi znaków.
 */

#include <string>
#include <iostream>

#include <boost/lexical_cast.hpp>

using std::string;
using std::cout;
using std::endl;
using std::ostream;
using std::istream;

using boost::lexical_cast;
using boost::bad_lexical_cast;

// Testuje lexical_cast dla wlasnej klasy
void testLcForOwnClass();

int main(int argc, char *argv[]) {
    // Utworzenie łańcucha znaków "1337" a następnie zamiana na liczbę 
    // całkowitą (int)
    string intStr("1337");
    int intVal = lexical_cast<int>(intStr);
    cout << "Wczytana wartosc typu int = " << intVal << endl;
    
    // Utworzenie łańcucha znaków "31.337" a następnie zamiana na liczbę 
    // rzeczywistą (float)
    string floatStr("31.337");
    float floatVal = lexical_cast<float>(floatStr);
    cout << "Wczytana wartosc typu float = " << floatVal << endl;

    // Konwersji do zmiennej typu bool można dokonać z ciągów znaków
    // "0", "1", "0.0", "1.0" a także pojedynczych znaków '0' i '1'.
    // Niestety w bieżącej wersji (1.35.0) nie ma możliwości zamiany 
    // napisów "true" i "false". Wypisanie zmiennej typu bool
    // daje w wyniki "1" dla true i "0" dla false.
    
    // Utworzenie łańcucha znaków "1" a następnie zamiana na zmienną typu
    // bool
    string trueStr("1");
    bool trueBool = lexical_cast<bool>(trueStr);
    cout << "Wczytano wartosc typu bool = "<< trueBool << endl;

    // Utworzenie pojedynczego znaku '0' a następnie zamiana na zmienną typu
    // bool

    char falseChar = '0';
    bool falseBool = lexical_cast<bool>(falseChar);
    cout << "Wczytano wartosc typu bool = "<< falseBool << endl;


    // Jeśli konwersja się nie powiedzie, wyrzucany jest wyjątek
    // boost::bad_lexical_cast
    // Próba konwersji ciągu znaków "blah" na liczbę całkowitą
    try {
        lexical_cast<int>("Test");
    } catch (bad_lexical_cast &e) {
        cout<<"Nieudana konwersja ciagu znakow \"Test\" na zmienna typu int, " << 
            "komunikat z wyjatku: " << e.what() << endl;
    }

    // Uwaga na pułapkę: konwersja do typu "char" lub "unsigned char" 
    // traktuje typ "char" lub "unsigned char" jako znakowy, nie numeryczny!
    // Operacja taka wczytuje bajt ze źródła, jeśli jednak ma ono więcej bajtów
    // jest wyrzucany wyjątek. Sytuacja taka może też wystąpić gdy spróbujemy 
    // rzutować na typ uint8_t, który jest zwykle zadeklarowany jako unsigned char.
    try {
        lexical_cast<unsigned char>("100");
    } catch (bad_lexical_cast &e) {
        cout << "Nieudana konwersja ciagu znakow \"100\" na zmienna " <<
            "typu unsigned char, komunikat z wyjatku: "<< e.what() << endl;
    }


    // Można też dokonać konwersji "w odwrotną stronę", to znaczy zamienić
    // liczbę na ciąg znaków lub pojedynczy znak.
    string year1970(lexical_cast<string>(1970));
    cout << "Wczytano string = " << year1970 << endl;

    char luckyNumber = (lexical_cast<char>(7));
    cout << "Wczytano char = " << luckyNumber << endl;
    
    // Na koniec testujemy lexical_cast dla wlasnej klasy
    testLcForOwnClass();

}

// Jak sprawić, by lexical_cast działał dla naszych klas?
// Załóżmy, że nasza klasa Integer opakowująca liczby naturalne 
// ma być celem (target) konwersji.
class Integer {
public:
    // Musi istnieć możliwość domyślnego skonstruowania obiektu 
    Integer(int value=0) : value_(value) {}
    
    // Musi istnieć konstruktor kopiujący (w tym przykładzie pozwalamy aby 
    // wygenerował go kompilator)
    //

    // Musi być zadeklarowany operator >> jako parametry przyjmujący 
    // istream lub wstream po lewej stronie, a po prawej instancję typu 
    // będącego rezultatem konwersji.
    friend istream& operator>>(istream& is, Integer& i) {
        return is >> i.value_;
    }

    /**
     * Zwraca wartość opakowywaną przez Integer
     */
    int getValue() {
        return value_;
    }
private:
    /** 
     * Wartość opakowywana
     */
    int value_;
};

void testLcForOwnClass() {
    Integer i;
    
    i = lexical_cast<Integer>("234");
    cout << "Wczytano wartosc Integer = " << i.getValue() << endl;
}

