Hilfe: Funktion in einer Timer-Klasse (void) soll einen Wert zurückgeben (C4716)



  • Hallo,
    ich möchte die Funktion Berechne() (ohne Rückgabewert) alle 500ms ausführen, allerdings kommt der folgende Fehler
    C4716 Timer::Berechne Muss einen Wert zurückgeben Timer.cpp Zeile 28
    Wo ist das Problem?
    Code Timer.h

    #ifndef TIMER_H
    #define TIMER_H
    
    #include <chrono>
    #include <functional>
    #include <thread>
    
    class Timer
    {
    public:
    	Timer();
    	~Timer();
    private:
    	void timer_start(std::function<void()> func, int interval);
    	std::function<void()> Berechne();
    private:
    	int timer;
    };
    
    #endif
    

    Timer.cpp

    #include "Timer.h"
    #include <sstream>
    #include <string>
    
    Timer::Timer()
    {
    	timer = 0;
    	timer_start(Berechne(), 500);
    }
    
    Timer::~Timer()
    {}
    
    void Timer::timer_start(std::function<void()> func, int interval)
    {
    	std::thread([func, interval]() {
    		while (true)
    		{
    			func();
    			std::this_thread::sleep_for(std::chrono::milliseconds(interval));
    		}
    		}).detach();
    }
    
    std::function<void()> Timer::Berechne()
    {
    //DoSomething
    }
    


  • @stefanpc81 sagte in Hilfe: Funktion in einer Timer-Klasse (void) soll einen Wert zurückgeben (C4716):

    Hallo,
    ich möchte die Funktion Berechne() (ohne Rückgabewert) alle 500ms ausführen, allerdings kommt der folgende Fehler
    C4716 Timer::Berechne Muss einen Wert zurückgeben Timer.cpp Zeile 28
    Wo ist das Problem?

    Hier:

    std::function<void()> Berechne();



  • Lese die Fehlermeldung. Wie wird aus einer Funktion bzw. Methode ein Wert zurückgegeben? Zauberwort: return ...;



  • @Belli sagte in Hilfe: Funktion in einer Timer-Klasse (void) soll einen Wert zurückgeben (C4716):

    std::function<void()>

    Sorry, ich stehe auf dem Schlauch... Ich bin noch relativ neu dabei, zu programmieren. Da die Funktion void ist, gibt es doch kein return ... Und return std::function<void()>; funktioniert auch nicht.



  • @stefanpc81
    Die Funktion ist nicht void - sie ist std::function<void()>

    std::function<void()> Berechne(); //nicht void
    void Berechne(); //void
    


  • Und bei

    timer_start(Berechne(), 500);
    

    möchtest du nur die Funktion übergeben und nicht die Funktion direkt ausführen, also

    timer_start(Berechne, 500);
    

    Wahrscheinlich hast du dann wegen der Fehlermeldung den Rückgabetyp fälschlicherweise geändert...



  • Hallo,
    danke soweit für die Hilfen. Ich weiß nicht, ob ich eure Lösungsvorschläge richtig verstanden habe:

    1. Sowie ich die Erklärung im Web verstanden habe, bedeutet die Verwendung von std::function<void()> dass die Funktion vom Typ void sein muss. Also muss ich folglich wie von @Belli void Berechne(); schreiben.
    2. @Th69 Die () im Konstruktor weglassen, da diese zu übergebende Funktion unter void Timer::timer_start(...) ausgeführt wird.
    3. Trotzdem kommen noch zwei Fehlermeldungen:
      C4716 Timer::Berechne Muss einen Wert zurückgeben Timer.cpp Zeile 28
      Leider immer noch. Ich konnte auch im Web keinen Rückgabetyp mit der Verwendung std::function<void()> finden bzw. wie gesagt: mit void gibt es doch gar kein return ... Ich verstehe nach wie vor nicht, was ich hier konkret schreiben soll. 😞
    4. Die zweite Fehlermdlung besagt
      E0415 Für eine Konvertierung von ""void ()"" in""std::function<void ()>"" ist kein passender Konstruktor vorhanden. Timer.cpp Zeile 8
      Ich habe jetzt 1,5 Stunden herumprobiert bzw. nichts im Web gefunden, was mir weiterhelfen könnte.


  • Dein Timer::Berechne gibt angeblich laut Signatur eine Funktion zurück, die 0 Parameter hat und nichts returnt. Gesucht ist aber eine Funktion, die nichts returnt und NICHT eine Funktion, die eine Funktion zurückgibt, die die passenden Eigenschaften hat. (*)

    Also nochmal:
    der Funktion void timer_start(std::function<void()> func, int interval); musst du 2 Parameter übergeben: ein ausführbares Ding mit 0 Parametern und mit void als Rückgabetyp sowie einen int.

    Du übergibst Berechne(), d.h. das, was die Funktion Berechne zurückgibt. In der Funktion befindet sich aber kein return. Was du stattdessen willst: die Funktion std::function<void()> Berechne() sollte void Berechne() sein - und dann übergibst du Berechne (die Funktion selbst) statt Berechne() (das Ergebnis der Funktion).

    Aber Achtung: dein Berechne ist eine Memberfunktion. Sie nimmt implizit einen this-Pointer als Argument. Du kannst also Berechne nicht übergeben, wenn die Fkt nicht static ist. Daher musst du den this-Pointer an die Funktion binden. Entweder mit bind oder indem du ein Lambda machst:

    timer_start([this](){ this->Berechne(); }, 500);

    Noch was Allgemeines zu deinem Code: Es bleibt noch eine andere Frage: wieso ist "Berechne" eine Member-Funktion des Timers?! Ein Timer sollte nur Timer-Dinge tun. Die Funktion Berechne sollte daher anderswo zu finden sein. Ein Timer sollte mit beliebigen Funktionen umgehen können.

    PS: (*) std::function macht type erasure und du kannst auch andere, kompatible Funktionen übergeben und insbesondere auch solche mit einem Returnwert. Aber das soll dich erstmal nicht interessieren.



  • Die Lösung mit bind müsste so aussehen:

    #ifndef TIMER_H
    #define TIMER_H
    
    #include <chrono>
    #include <functional>
    #include <thread>
    
    class Timer
    {
    public:
    	Timer();
    	~Timer();
    private:
    	void timer_start(std::function<void()> func, int interval);
    	void Berechne();
    private:
    	int timer;
    };
    
    #endif
    
    #include "Timer.h"
    #include <sstream>
    #include <string>
    
    Timer::Timer()
    {
    	timer = 0;
    	std::function<void()> f = std::bind(&Timer::Berechne, this);
    	timer_start(f, 500);
    }
    
    Timer::~Timer()
    {}
    
    void Timer::timer_start(std::function<void()> func, int interval)
    {
    	std::thread([func, interval]() {
    		while (true)
    		{
    			func();
    			std::this_thread::sleep_for(std::chrono::milliseconds(interval));
    		}
    		}).detach();
    }
    
    void Timer::Berechne()
    {
    //DoSomething
    }
    


  • Also irgendwie ist das schon eine recht komische Timer Implementierung. Es wird eine Klasse verwendet, aber kein RAII und deswegen wohl auch das thread.detach(). Entkoppelte Threads sind in der Regel immer "böse", sollte man einfach nicht machen. Falls du C++17 verwenden kannst, kann man daraus ein recht brauchbares Template machen. So spontan hätte ich mir den Timer so gebastelt, wenn ich keine großen Anforderungen habe.

    #include <chrono>
    #include <functional>
    #include <iostream>
    #include <thread>
    
    template <bool Async = true, bool Joinable = true>
    struct Timer {
        template <typename Callable, typename... Args>
        Timer(const size_t wait_ms, Callable &&func, Args &&...args)
        {
            auto task(std::bind(std::forward<Callable>(func), std::forward<Args>(args)...));
    
            if constexpr (Async)
            {
                _thread = std::thread([wait_ms, task]()
                {
                    std::this_thread::sleep_for(std::chrono::milliseconds(wait_ms));
                    task();
                });
    
                if constexpr (!Joinable)
                    _thread.detach();
            }
            else
            {
                std::this_thread::sleep_for(std::chrono::milliseconds(wait_ms));
                task();
            }
        }
    
        ~Timer()
        {
            if (Joinable && _thread.joinable())
                _thread.join();
        }
    
        std::thread _thread;
    };
    

    Und dieses Timer Template kann man dann so benutzen:

    void test(const char *arg)
    {
        std::cout << arg << std::endl;
    }
    
    int32_t main()
    {
        Timer timer1(2000, test, "timer1");
        Timer timer2(1000, test, "timer2");
    
        return 0;
    }
    

    Dieses einfache Template unterstützt keine Rückgabewerte. Man kann aber das Template nehmen und statt alles im Constructor zu machen, schiebt man einfach alles zum Beispiel in den function call operator (operator()). Dann muss man sich noch aus einem Promise/Future einen Rückgabewert-Container basteln und tataaa, man kann sich auch Rückgabewerte geben lassen. Wenn man es noch etwas bunter will, kann man auch mit condition variables das Ganze abbrechbar machen (sleep_for durch condition_variable::wait_for Konstruktion ersetzen).


Log in to reply