Announcement

Collapse
No announcement yet.

Pass non-UObject to UFunction for FTimerDelegate (cppkafka lib)

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    Pass non-UObject to UFunction for FTimerDelegate (cppkafka lib)

    I'm building a data pipeline with UE4 at the end for visualization and AR/VR interactivity. There's a lot of data and processing to prepare it for UE4 so the pipeline is built around Kafka. I have been working on importing the cppkafka library (https://github.com/mfontanini/cppkafka) - a wrapper for the librdkafka Kafka library - into UE4. It's been challenging, but I thought it was mostly there until a few days ago.

    I'm using an FTimer driven function to poll for new messages every n seconds, which needs a cppkafka::Consumer object to do the polling. I want to pass the Consumer object into the timer function so that I don't need to keep setting it up and subscribing to topics every time the timer fires, and this is where things break down.

    Trying to compile gives me the error: Cannot find class '', to resolve delegate 'Consumer'. Is what I'm trying to do possible? Can I pass a non-UObject object into a UFunction? If so, any advice on how to accomplish this? And if not, are there any options to achieve the same outcome in a different way?

    BTW, a barebones project is at https://github.com/oluf/DataStreaming_UE4. It's mostly useless without some documentation and context, but if anyone is interested let me know, I'm happy to share what I've learned.


    Code:
    //    AConsumerOne header (actor class)
    
    UFUNCTION()
    void ConsumerRunLoop(cppkafka::Consumer consumer);
    Code:
    //    AConsumerOne implementation (actor)
    
    void AConsumerOne::BeginPlay()
    {
        Super::BeginPlay();
    
        cppkafka::Configuration kafkaConfig = {
        { "metadata.broker.list", "192.168.0.179:9092" },
        //    { "debug", "all" },
        { "group.id", "defaultgroup" }
        };
    
        cppkafka::Consumer kafkaConsumer(kafkaConfig);
        kafkaConsumer.subscribe({ "test" });
    
        FTimerDelegate ConsumerTimerDelegate;
        ConsumerTimerDelegate.BindUFunction(this, FName("ConsumerRunLoop"), &kafkaConsumer);
    
        FTimerHandle ConsumerTimerHandle;
        GetWorldTimerManager().SetTimer(ConsumerTimerHandle, ConsumerTimerDelegate, 5.f, true, 0.f);    
    }

    Code:
    void AConsumerOne::ConsumerRunLoop(cppkafka::Consumer consumer) {
        cppkafka::Message msg = consumer.poll();
    
        //    Do stuff here
    }
    Last edited by Oluf_A; 05-14-2019, 10:33 AM.

    #2
    You can't have a UFUNCTION in a non-reflected class. You need to use BindRaw for native CPP functions.

    Comment


      #3
      Thanks for replying Jamsh.

      I'm not following or I wasn't clear. I can't see how to apply BindRaw in this case.

      UFUNCTION is applied to a function in an Actor class that will be called by a timer, I'm not trying to apply UFUNCTION to a function in a native cpp class. I can create the native cpp cppkafka::Consumer object in my actor class, I just can't pass it into a UFUNCTION in that same actor class.

      Oluf

      Comment


        #4
        Code:
        cppkafka::Consumer consumer
        If you want to use that object that way, wrap it within a USTRUCT(BlueprintType) then pass that struct as UFunction params.
        When accessing from ConsumerRunLoop() you can call myStruct.consumer.poll();
        | Savior | USQLite | FSM | Object Pool | Sound Occlusion | Property Transfer | Magic Nodes | MORE |

        Comment


          #5
          Thanks BrUnO XaVIeR,

          I tried your suggestion but I'm still getting the Cannot find class '', to resolve delegate 'Consumer' error, in the header where I declared the struct. It just won't let me declare an object of cppkafka::Consumer without passing a cppkafka::Configuration object to the constructor. I guess I can build a new constructor, but that's something I'm not able to do right now. I'm going to rethink the approach.

          Thanks for your input.

          Comment


            #6
            There are two things wrong in your code.

            Code:
             cppkafka::Consumer kafkaConsumer(kafkaConfig);
            When you create your class like this, you are creating it on the stack. Once you are out of the BeginPlay function, the class will be destroyed and your timer then cannot call anything other than passing as value. I am not even sure if it can successfully handle this internally.

            Code:
            cppkafka::Consumer* kafkaConsumerPtr;
            and on your BeginPlay you should do:

            Code:
            kafkaConsumerPtr = new cppkafka::Consumer(kafkaConfig);
            Now to your actual issue, Unreal doesn't do well with passing objects as values. I quite believe that if you did the following, your issues should go away:

            .h file

            Code:
             void ConsumerRunLoop(cppkafka::Consumer* consumer);
            without UFUNCTION, and do BindRaw as

            Code:
             // Not entirely sure about the signature of BindRaw
            
            ConsumerTimerDelegate.BindRaw(this, &AConsumerOne::ConsumerRunLoop, kafkaConsumerPtr);
            it should do.

            I couldnt really understand why do you pass the object as a reference/value but this should help.

            Edit: Just realized, if you store the pointer on the class itself (Which looks like it could be done given it looks like you wanna have one object per actor), you can as well just convert the function to have no parameters and go with BindUFunction

            Edit 2: For your reply to TheJamsh, it is about having a non-unreal class (UObjects, basic types, Unreal Structs etc.) in either the parameters or the return val of a function.
            Last edited by Naocan; 05-16-2019, 03:54 AM.

            Comment


              #7
              Thanks a lot Naocan, that cleared up a lot. I'm able to get access to the consumerPtr from ConsumerRunLoop() now. I set it up with no parameters and just accessed consumerPtr as a member. I thought I had tried that but couldn't get it working, but this did it.

              Comment

              Working...
              X