Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Login

    ObservableFinished() does not pass a job result to an observer

    Cinema 4D SDK
    c++ r21 windows
    2
    5
    959
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • B
      Braeburn
      last edited by

      Hi everyone,

      I would like to use Cinemas job system to send multiple HTTP requests asynchronous / in parallel. I have created a custom HttpRequestJob which will return a custom HttpResponse struct.

      After the job will be enqueued (and therefore started) I can sucessfully ask for the HTTP response with:

      HttpResponse response = httpRequestJob.MoveResult() iferr_return;

      Instead of asking the job for a result I would like to get a callback function called after the job has finished, because this would better fit into our current plugin architecture. I would like to add an observer to httpRequestJob.ObservableFinished().

      The problem is:
      I will not get the result from the job (an HttpResponse object) passed to the observer / callback function. The callback function has the following signature:

      static void RequestCallback(HttpResponse httpResponse)

      It seems the following does not work because the callback function has a parameter:

      httpRequestJob.ObservableFinished().AddObserver(RequestCallback);

      It also does not work when I provide a lambda with a HttpResponse or const HttpResponse& argument as an observer:

      httpRequestJob.ObservableFinished().AddObserver([](HttpResponse response)
      {
         // Doing something with the response ...
      }) iferr_return;
      

      It seems AddObserver() will only take a callback function without arguments in this case, but this way I cannot do any meaningful work with the observer.

      Any ideas how I can get access to the job result in an observer? Thanks in advance!

      Best regards,

      Tim

      PS: The job looks something like this:

      class HttpRequestJob : public maxon::JobInterfaceTemplate<HttpRequestJob, HttpResponse>
      {
      public:
      	HttpRequestJob() { };
      
      	HttpRequestJob(
      		const String &url,
      		const String &method,
      		const String &body,
      		const maxon::DataDictionary &headers)
      	{
      		_url = url;
      		_method = method;
      		_body = body;
      		_headers = headers;
      	}
      
      	maxon::Result<void> operator ()()
      	{
      		iferr (HttpResponse httpResponse = SendHttpRequest(_url, _method, _body, _headers, _description))
      		{
      			ApplicationOutput("@", err);
      			return err;
      		}
      
      		return SetResult(std::move(httpResponse));
      	}
      
      	const maxon::Char* GetName() const
      	{
      		return "HttpRequestJob";
      	}
      
      private:
      	maxon::String _url;
      	maxon::String _method;
      	maxon::String _body;
      	maxon::DataDictionary _headers;
      };
      
      1 Reply Last reply Reply Quote 0
      • ManuelM
        Manuel
        last edited by Manuel

        hi,

        You can add a lambda and capture this so you will have access to your job's class. as if you were on the same part of your code.
        This doesn't answer the question if you need to pass a static function with a parameter. For that i would need a bit more searching and test.
        Also you can simply call the function you want with the parameter you want from the lambda.

        httpRequestJob.ObservableFinished().AddObserver([this]()
        {
        	iferr_scope;
           	HttpResponse result =  httpRequestJob.GetResult() iferr_return
                ...
        
        }) iferr_return;
        

        Cheers,
        Manuel

        MAXON SDK Specialist

        MAXON Registered Developer

        B 1 Reply Last reply Reply Quote 0
        • B
          Braeburn @Manuel
          last edited by Braeburn

          Thanks for your reply Manuel (@m_magalhaes)!

          One of my first ideas for a solution was to use a this capture for the lambda, but in my case the function which creates the HttpRequestJob and adds the Observer is a static function and I got the following compiler error: 'this' can only be used as a lambda capture within a non-static member function.
          So I replaced the this capture with an & capture:

          httpRequestJob.ObservableFinished().AddObserver([&]()
          {
          	iferr (HttpResponse response = httpRequestJob.GetResult())
          	{
          		ApplicationOutput("Error: @", err);
          	}
          
                  ...
          
          }) iferr_return;
          

          This will compile, but GetResult() returns the following error at runtime: nullptr [job.h(1354)] The returned HttpResponse will be empty / uninitialized. It seems the result might not exist anymore when the observer gets called. httpRequestJob.GetResult() works fine if called outside of the lambda, so I had the feeling that getting the result inside the lambda was not the right way to go. I was expecting that ObservableFinished() would pass the result instead.

          Any more ideas?

          Best regards,
          Tim

          1 Reply Last reply Reply Quote 0
          • ManuelM
            Manuel
            last edited by

            hi,

            sorry for the delay, i was trying different solutions.

            I simply forgot the simple one.

            In fact, in your lambda, when capturing scope variable by reference with & it's not working because once the job have finished, the reference count go to 0 and the job is destroyed.
            Instead, if you pass the job as "copy" the reference count will rise by one so the job will not be destroyed. Instead it will be destroyed once the lambda is finished.
            So you can retrieve the result and maybe send it to another function or do something with it.

            struct HttpResponse 
            {
            	Int32 firstArgument = NOTOK;
            	Int32 secondArgument = NOTOK;
            };
            
            
            class HttpRequestJob : public maxon::JobInterfaceTemplate<HttpRequestJob, HttpResponse>
            {
            public:
            	HttpRequestJob() { };
            
            	MAXON_IMPLICIT HttpRequestJob(const Int32 in_first, const Int32 in_second) : first(in_first), second(in_second) {};
            	
            	maxon::Result<void> operator ()()
            	{
            		iferr_scope;
            		HttpResponse httpResponse{ first * second, second * first };
            		return SetResult(std::move(httpResponse));
            	}
            
            	const maxon::Char* GetName() const
            	{
            		return "HttpRequestJob";
            	}
            
            private:
            	Int32 first = NOTOK;
            	Int32 second = NOTOK;
            };
            
            
            static maxon::Result<void> pc12926(BaseDocument* doc)
            {
            	iferr_scope;
            
            	for (Int32 i = 0; i < 10; i++)
            	{
            		auto connection = HttpRequestJob::Create(i, i + 10) iferr_return;
            		connection.ObservableFinished().AddObserver([connection]() -> maxon::Result<void>
            			{
            				iferr_scope;
            				HttpResponse res = connection.GetResult() iferr_return;
            				ApplicationOutput("result are @ and @", res.firstArgument, res.secondArgument);
            				return maxon::OK;
            			})iferr_return;
            
            		connection.Enqueue();
            	}
            	
            	return maxon::OK;
            }
            

            Cheers,
            Manuel

            MAXON SDK Specialist

            MAXON Registered Developer

            B 1 Reply Last reply Reply Quote 0
            • B
              Braeburn @Manuel
              last edited by Braeburn

              Thank you very much, Manuel!
              This is a working solution for the issue and it helped me a lot!

              Best regards,
              Tim

              1 Reply Last reply Reply Quote 0
              • First post
                Last post