Funktion mit verschiedenen return Typen möglich?



  • Hallo Forum,

    ich möchte in einer Funktion entweder einen float Wert zurückgebe oder eine Zeichenkette (bzw. Zeiger auf eine Zeichenkette).
    Beispiel:

    test(a,b)
      if( a/b ==1)
     {
        return "OK";
    }
     else
    {
      return static_cast<float> (a)/b;
    }
    

    Das ganze soll dann mit

    cout <<  test(x,y)
    

    ausgegeben werden. a, b sind integer-Werte

    Gibt es dafür eine elegante Lösung in C++, die auch für nicht C++ Experten halbwegs verständlich ist?
    Oder muss ich das irgendwie mit einer übergebenen Stuktur bzw. mit zusätzlichen, per Referenz übergebenen Funktionsparametern umsetzen?

    Danke für jede Hilfe!



  • std::variant.

    #include <variant>
    #include <string>
    #include <iostream>
    
    std::variant<float, std::string> test(int a, int b)
    {
    	auto result{ a / static_cast<float>(b) };
    	if (result == 1)
    		return "Ok";
    	return result;
    }
    
    std::ostream& operator<<(std::ostream &os, std::variant<float, std::string> const &var)
    {
    	try {
    		os << std::get<float>(var);
    	}
    	catch (std::bad_variant_access&) {
    		os << std::get<std::string>(var);
    	}
    
    	return os;
    }
    
    int main()
    {
    	std::cout << test(4, 4) << '\n' << test(4, 2) << '\n';
    }
    


  • @Swordfish
    Looks great an extraordinary for a C++ newbie like me.

    But unfortunately my compiler does not support <variant> which seems to be a relatively new extension of the C++ standard.
    <string> header my compiler provides, also include-files like <vector> i can see. It's a compiler for embedded programming.
    I have to find out within the manual, which C++ standard my compiler supports and then I have to mention this in my question(s).

    In any case: Thanks for your instructive source code example!



  • Plan-B:

    #include <string>
    #include <iostream>
    
    class float_string_t
    {
    	union {
    		float f;
    		std::string str;
    	};
    	bool is_string;
    
    public:
    	float_string_t(float f) : f{ f }, is_string{ false } {}
    	float_string_t(std::string const &str) : str{ str }, is_string{ true } {}
    
    	~float_string_t()
    	{
    		if (is_string)
    			str.~string();
    	};
    
    	bool is_float() const { return !is_string; }
    	float get_float() const { return f; }
    	std::string get_string() const { return str; }
    };
    
    
    float_string_t test(int a, int b)
    {
    	auto result{ a / static_cast<float>(b) };
    	if (result == 1)
    		return std::string{ "Ok" };
    	return result;
    }
    
    std::ostream& operator<<(std::ostream &os, float_string_t const &var)
    {
    	if (var.is_float())
    		return os << var.get_float();
    	return os << var.get_string();
    }
    
    int main()
    {
    	std::cout << test(4, 4) << '\n' << test(4, 2) << '\n';
    }
    


  • @Swordfish sagte in Funktion mit verschiedenen return Typen möglich?:

    Plan-B:

    #include <string>
    #include <iostream>
    
    class float_string_t
    {
    	union {
    		float f;
    		std::string str;
    	};
    	bool is_string;
    
    public:
    	float_string_t(float f) : f{ f }, is_string{ false } {}
    	float_string_t(std::string const &str) : str{ str }, is_string{ true } {}
    	~float_string_t() {};
    
    	bool is_float() const { return !is_string; }
    	float get_float() const { return f; }
    	std::string get_string() const { return str; }
    };
    

    schauder

    Der Dtor leaked. Copy-Ctor und Assignment-Operator gibt's keine weil sie nicht implizit definiert werden dürfen (union member mit nicht-trivialem Copy-Ctor/Assignment-Operator).

    Natürlich könnte man jetzt anfangen das alles selbst zu implementieren, aber das ist halt etwas fummelig. Und das alles um potentiell die 4 Byte für den float zu sparen. Zahlt sich nicht aus. Dann lieber ohne Gewerkschaft.



  • @hustbaer sagte in Funktion mit verschiedenen return Typen möglich?:

    Der Dtor leaked.

    Huch? Wie ginge das dann?

    @hustbaer sagte in Funktion mit verschiedenen return Typen möglich?:

    Dann lieber ohne Gewerkschaft.

    lach



  • @Swordfish sagte in Funktion mit verschiedenen return Typen möglich?:

    Huch? Wie ginge das dann?

    Naja, woher soll der Compiler wissen, ob er den Destruktor von f oder den von str ausführen soll? Du musst das von Hand machen (if (isString()) str.~string();, urgh - oder so ähnlich)

    Ein Union-Beispiel mit int, float und string findet sich sogar im C++-Standard. In N4659 in Abschnitt 12.3 Absatz 4.



  • @wob Ach so, ja, das klingt logisch. Find' ich aber halb so schlimm.



  • @tecranovis
    Das macht in deinem Fall keinen Sinn.
    Entweder erschlägst du das über ein std::pair oder du überlegst dir was dein Rückgabewert "OK" eigentlich aussagt und ersetzt ihn vielleicht durch ein bool, falls er eigentlich nur aussagt "irgendwas hat hier gut funktioniert oder eben nicht".

    Die Funktion könnte dann auch so aussehen:

    std::pair<double,bool> test( float a, float b )
    {
        if ( a / b == 1 )
            return std::make_pair( true, 0 );
        else if ( b != 0 )
            return std::make_pair( false, a / b );
        else
            return std::make_pair( false, -1 ); // hier sind auch noch andere Arten der Fehlerrückgabe möglich )
    }
    

    Wenn du mehr als nur "true und false" aussagen willst, kannst du auch noch andere Datentype ( char, short, oder enums ) nutzen, um z.B. drei Aussagen zu treffen.

    Die Hauptfrage ist also: Was willst du neben der Rückgabe des mathematischen Rechenergebnisses noch aussagen.



  • @It0101

    Hallo,

    die Ausgaben landen in einer Datei, die von Personen betrachtet wird. Und da ist der Text "OK" einfach besser erfassbar als der Zahlenwert 1 denke ich.
    Im nicht OK Fall wiederum ist im meimen Fall die Ausgabe des Quotienten aussagekräftiger.

    Man könnte das sicher auch irgedwie im cout direkt abfragen mit a/b==1? "OK": a/b aber da ich recht viele solcher Ausgaben habe wollte ich das irgenwie eleganter umsetzen.

    Danke dir und den anderen Foristen für eure Zeit und Hilfe!



  • Wenn das Ganze in ´ner Datei landet, das Ergebnis nicht weiter verwendet wird und die Datei nur von anderen gelesen wird kannste auch sofort std::string benutzen. Wenn das Ergebnis nicht dem erwarteten Wert entspricht konvertierste das in ´nen string und gibst den zurück.



  • ... oder Du schreibst direkt in der Funktion in die Datei. Wenn der weitere Programmverlauf vom Funktionsergebnis unabhängig ist, brauchst Du gar keinen Rückgabewert ...



  • @tecranovis
    Alle bisherigen Antworten hatten zum Ziel, irgendwas zu basteln, damit eine Funktion 2 Werte zurück gibt.
    Ich glaube niemand hier hätte dir das als Tipp gegeben, um eine Ausgabe zu formatieren.
    Da kannst du doch besser eine Funktion nehmen:

    toStream(double value, std::ostream& os)
    {
      if(value ==1)
         os << "OK";
      else
        os << value;
    }
    ...
    
    
    


  • @Jockelx sagte in Funktion mit verschiedenen return Typen möglich?:

    @tecranovis
    Alle bisherigen Antworten hatten zum Ziel, irgendwas zu basteln, damit eine Funktion 2 Werte zurück gibt.
    Ich glaube niemand hier hätte dir das als Tipp gegeben, um eine Ausgabe zu formatieren.
    Da kannst du doch besser eine Funktion nehmen:

    
    
      if(value ==1)
     
    

    Ich hab gehört, mit solchen Vergleichen sollte man vorsichtig sein, wenn es um Fließkommazahlen geht ...



  • @Belli

    Ich habe gehört, dass man nicht eigenmächtig eigene/neue Logik einbaut, wenn man etwas in eine Funktion auslagert 😉



  • @Jockelx @Belli Ich habe gehört, daß a und b als ints übergeben werden

    if (a == b)
        return "Ok";
    

    also:

    std::ostream& magic_div(std::ostream &os, int a, int b)
    {
        if (a == b)
            return os << "Ok";
        return os << (a / static_cast<float>(b));
    )
    


  • @Swordfish Du hast wohl richtig gehört!
    Ich hab einfach nur nach oben gescrollt und den ersten Code (von @It0101 ) übernommen.
    Im ersten Post steht aber was von int.

    Deine Funktion finde ich aber trotzdem nicht gut. Die komplizierte Berechnung (a/b) würde ich in einer eigenen Funktion machen und die stream-Ausgabe in einer Anderen (mit dem berechneten Wert).



  • @Jockelx Dann musst wieder mit einem epsilon rumspielen ...



  • @Swordfish Mmh, ja, ist auch doof. Stimmt schon.



  • @Jockelx sagte in Funktion mit verschiedenen return Typen möglich?:

    Ich hab einfach nur nach oben gescrollt und den ersten Code (von @It0101 ) übernommen.

    Nein. Der Code des TE enthält keinerlei Datentypen, nur den static_cast auf float. Die Ints sind eine Erfindung vom Aquarium-Insassen 😉


Anmelden zum Antworten