<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:media="http://search.yahoo.com/mrss/">
  <channel>
    <title>cast on stillcooking.dev</title>
    <link>https://stillcooking.dev/en/tags/cast/</link>
    <description>Personal portfolio and technical notes</description>
    <generator>Hugo</generator>
    <language>en-US</language>
    
    <copyright>© 2026 stillcooking.dev — CC BY 4.0, https://creativecommons.org/licenses/by/4.0/</copyright>
    <lastBuildDate>Tue, 07 Jul 2026 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://stillcooking.dev/en/tags/cast/index.xml" rel="self" type="application/rss+xml" />
    
    <item>
      <title>Casting to an interface misses Blueprint implementations</title>
      <link>https://stillcooking.dev/en/topics/unreal-engine/cpp-in-unreal/interface-cast-misses-blueprint/</link>
      <pubDate>Tue, 07 Jul 2026 00:00:00 +0000</pubDate>
      
      <guid isPermaLink="true">https://stillcooking.dev/en/topics/unreal-engine/cpp-in-unreal/interface-cast-misses-blueprint/</guid>
      <description>Cast&lt;IMyInterface&gt;(Obj) returns nullptr when an object&amp;rsquo;s interface implementation exists only in Blueprint, even though ImplementsInterface() returns true for that same object. The engine skips entries marked bImplementedByK2, so casting to an interface is safe only when that interface is explicitly blocked from Blueprint implementation.</description>
      <content:encoded><![CDATA[<h2 id="two-ways-to-call">Two ways to call</h2>
<p>There are two ways to call an interface function in Unreal: cast to the <code>I*</code> type or use the generated <code>Execute_</code> wrapper. They look interchangeable, but they ask the engine two different questions.</p>
<p><code>Cast&lt;IMyInterface&gt;(Obj)</code> returns <code>nullptr</code> for an object whose interface implementation exists only in Blueprint. For that same object, <code>Obj-&gt;GetClass()-&gt;ImplementsInterface(UMyInterface::StaticClass())</code> returns <code>true</code>. The engine deliberately skips interface entries added by the Blueprint compiler, so casting to <code>I*</code> is safe only when the interface is explicitly marked as non-implementable in Blueprint. Everywhere else, it is a silent bug waiting for the day a designer creates a Blueprint implementation.</p>
<p>The whole problem comes down to one specific configuration: a native interface declared in C++ and implemented in Blueprint.</p>
<h2 id="two-functions-one-condition-apart">Two functions, one condition apart</h2>
<p><code>GetInterfaceAddress</code> and <code>ImplementsInterface</code> both walk the same <code>UClass::Interfaces</code> array and test the same relationship with <code>IsChildOf</code>. Exactly one extra condition separates them.</p>
<p><code>UObjectBaseUtility::GetInterfaceAddress</code> (<code>Engine/Source/Runtime/CoreUObject/Private/UObject/UObjectBaseUtility.cpp</code>), in the branch for a native interface:</p>
<div class="highlight" data-lang="cpp">
  <button type="button" class="code-copy" data-code-copy data-label="Copy" data-copied="Copied!" aria-label="Copy code to clipboard">Copy</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">for</span> <span class="p">(</span><span class="n">TArray</span><span class="o">&lt;</span><span class="n">FImplementedInterface</span><span class="o">&gt;::</span><span class="n">TIterator</span> <span class="n">It</span><span class="p">(</span><span class="n">CurrentClass</span><span class="o">-&gt;</span><span class="n">Interfaces</span><span class="p">);</span> <span class="n">It</span><span class="p">;</span> <span class="o">++</span><span class="n">It</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// See if this is the implementation we are looking for, and it was done natively, not in K2
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">FImplementedInterface</span><span class="o">&amp;</span> <span class="n">ImplInterface</span> <span class="o">=</span> <span class="o">*</span><span class="n">It</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span> <span class="o">!</span><span class="n">ImplInterface</span><span class="p">.</span><span class="n">bImplementedByK2</span> <span class="o">&amp;&amp;</span> <span class="n">ImplInterface</span><span class="p">.</span><span class="n">Class</span><span class="o">-&gt;</span><span class="n">IsChildOf</span><span class="p">(</span><span class="n">InterfaceClass</span><span class="p">)</span> <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Result</span> <span class="o">=</span> <span class="p">(</span><span class="n">uint8</span><span class="o">*</span><span class="p">)</span><span class="k">this</span> <span class="o">+</span> <span class="n">It</span><span class="o">-&gt;</span><span class="n">PointerOffset</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p><code>UClass::ImplementsInterface</code> (<code>Engine/Source/Runtime/CoreUObject/Private/UObject/Class.cpp</code>), with the same loop:</p>
<div class="highlight" data-lang="cpp">
  <button type="button" class="code-copy" data-code-copy data-label="Copy" data-copied="Copied!" aria-label="Copy code to clipboard">Copy</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">for</span> <span class="p">(</span><span class="n">TArray</span><span class="o">&lt;</span><span class="n">FImplementedInterface</span><span class="o">&gt;::</span><span class="n">TConstIterator</span> <span class="n">It</span><span class="p">(</span><span class="n">CurrentClass</span><span class="o">-&gt;</span><span class="n">Interfaces</span><span class="p">);</span> <span class="n">It</span><span class="p">;</span> <span class="o">++</span><span class="n">It</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="n">UClass</span><span class="o">*</span> <span class="n">InterfaceClass</span> <span class="o">=</span> <span class="n">It</span><span class="o">-&gt;</span><span class="n">Class</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">InterfaceClass</span> <span class="o">&amp;&amp;</span> <span class="n">InterfaceClass</span><span class="o">-&gt;</span><span class="n">IsChildOf</span><span class="p">(</span><span class="n">SomeInterface</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>The entire difference is <code>!ImplInterface.bImplementedByK2</code>. That flag marks an entry produced by the Blueprint compiler, and the engine&rsquo;s own comment says exactly that: <em>&ldquo;and it was done natively, not in K2.&rdquo;</em> <code>ImplementsInterface</code> has no such filter, so it gives the broader answer: the class implements the interface. <code>GetInterfaceAddress</code> gives a narrower answer: the class implements the interface <strong>natively</strong>.</p>
<div class="callout callout--insight">
  <div class="callout__title">Insight</div>
  <code>ImplementsInterface</code> asks whether the class implements the interface. <code>GetInterfaceAddress</code>, the function behind <code>Cast&lt;I*&gt;</code>, asks a narrower question: whether the class implements it <strong>natively</strong>. For a class whose interface implementation exists only in Blueprint, the first returns <code>true</code>, while the second returns <code>nullptr</code>.
</div>

<p>And <code>GetInterfaceAddress</code> is the cast. Not figuratively but literally, in <code>Engine/Source/Runtime/CoreUObject/Public/Templates/Casts.h</code>:</p>
<div class="highlight" data-lang="cpp">
  <button type="button" class="code-copy" data-code-copy data-label="Copy" data-copied="Copied!" aria-label="Copy code to clipboard">Copy</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">if</span> <span class="nf">constexpr</span> <span class="p">(</span><span class="n">TIsIInterface</span><span class="o">&lt;</span><span class="n">To</span><span class="o">&gt;::</span><span class="n">Value</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">(</span><span class="n">To</span><span class="o">*</span><span class="p">)((</span><span class="n">UObject</span><span class="o">*</span><span class="p">)</span><span class="n">Src</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">GetInterfaceAddress</span><span class="p">(</span><span class="n">To</span><span class="o">::</span><span class="n">UClassType</span><span class="o">::</span><span class="n">StaticClass</span><span class="p">());</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>A cast to an interface never touches the class-cast-flag path used by a plain <code>Cast&lt;AActor&gt;</code>. Instead, it calls a function designed specifically to exclude Blueprint implementations.</p>
<h2 id="why-there-is-nothing-to-return">Why there is nothing to return</h2>
<p>This looks like an oversight. It is a constraint of the memory layout.</p>
<p>A C++ class that implements <code>IMyInterface</code> inherits from it, so its memory layout contains an <code>IMyInterface</code> subobject at a known offset. That offset is exactly what the cast is built on: <code>(uint8*)this + It-&gt;PointerOffset</code>. If the interface is implemented only in Blueprint, there is no corresponding native <code>IMyInterface</code> subobject, so there is nothing for <code>PointerOffset</code> to describe. Without that subobject, there is no <code>IMyInterface</code> vtable either, and therefore no address for the cast to return.</p>
<p>A Blueprint implementation exists in a completely different form. It is a <code>UFunction</code> on the Blueprint class, invoked through the virtual machine. No valid pointer exists, so <code>nullptr</code> is the only honest answer.</p>
<p><code>Cast</code> itself is what makes this misleading: it looks like a uniform reflection mechanism, but in this case it is really asking about native memory layout.</p>
<h2 id="the-fix-execute_">The fix: <code>Execute_</code></h2>
<p>For every interface function, UHT generates a static <code>Execute_Foo</code> wrapper. <code>AppendInterfaceCallFunction</code> (<code>Engine/Source/Programs/Shared/EpicGames.UHT/Exporters/CodeGen/UhtHeaderCodeGeneratorCppFile.cs</code>) emits the body, which expands to this:</p>
<div class="highlight" data-lang="cpp">
  <button type="button" class="code-copy" data-code-copy data-label="Copy" data-copied="Copied!" aria-label="Copy code to clipboard">Copy</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">static</span> <span class="n">FName</span> <span class="n">NAME_UMyInterface_Foo</span> <span class="o">=</span> <span class="n">FName</span><span class="p">(</span><span class="n">TEXT</span><span class="p">(</span><span class="s">&#34;Foo&#34;</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">IMyInterface</span><span class="o">::</span><span class="n">Execute_Foo</span><span class="p">(</span><span class="n">UObject</span><span class="o">*</span> <span class="n">O</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">check</span><span class="p">(</span><span class="n">O</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">check</span><span class="p">(</span><span class="n">O</span><span class="o">-&gt;</span><span class="n">GetClass</span><span class="p">()</span><span class="o">-&gt;</span><span class="n">ImplementsInterface</span><span class="p">(</span><span class="n">UMyInterface</span><span class="o">::</span><span class="n">StaticClass</span><span class="p">()));</span>
</span></span><span class="line"><span class="cl">    <span class="n">UFunction</span><span class="o">*</span> <span class="k">const</span> <span class="n">Func</span> <span class="o">=</span> <span class="n">O</span><span class="o">-&gt;</span><span class="n">FindFunction</span><span class="p">(</span><span class="n">NAME_UMyInterface_Foo</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">Func</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">O</span><span class="o">-&gt;</span><span class="n">ProcessEvent</span><span class="p">(</span><span class="n">Func</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span> <span class="nf">if</span> <span class="p">(</span><span class="k">auto</span> <span class="n">I</span> <span class="o">=</span> <span class="p">(</span><span class="n">IMyInterface</span><span class="o">*</span><span class="p">)(</span><span class="n">O</span><span class="o">-&gt;</span><span class="n">GetNativeInterfaceAddress</span><span class="p">(</span><span class="n">UMyInterface</span><span class="o">::</span><span class="n">StaticClass</span><span class="p">())))</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">I</span><span class="o">-&gt;</span><span class="n">Foo_Implementation</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>The order is the opposite of what you would expect. <code>FindFunction</code> comes first. That is the reflection path, and it finds a Blueprint override just as well as a UHT-registered native function. Only when there is no <code>UFunction</code> does the wrapper retrieve the native subobject address and call <code>_Implementation</code> directly.</p>
<p><code>Execute_</code> gates itself on <code>ImplementsInterface</code>, the broader check, not on the existence of an interface address.</p>
<p>The correct call site therefore looks like this:</p>
<div class="highlight" data-lang="cpp">
  <button type="button" class="code-copy" data-code-copy data-label="Copy" data-copied="Copied!" aria-label="Copy code to clipboard">Copy</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">Obj</span><span class="o">-&gt;</span><span class="n">Implements</span><span class="o">&lt;</span><span class="n">UMyInterface</span><span class="o">&gt;</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">IMyInterface</span><span class="o">::</span><span class="n">Execute_Foo</span><span class="p">(</span><span class="n">Obj</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>A second safeguard sits in the native body of the event function itself. For interfaces, UHT emits that body as a hard assertion:</p>
<div class="highlight" data-lang="cpp">
  <button type="button" class="code-copy" data-code-copy data-label="Copy" data-copied="Copied!" aria-label="Copy code to clipboard">Copy</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">check</span><span class="p">(</span><span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="s">&#34;Do not directly call Event functions in Interfaces. Call Execute_Foo instead.&#34;</span><span class="p">);</span></span></span></code></pre></div></div>
<p>That gives <code>Cast&lt;IMyInterface&gt;(Obj)-&gt;Foo()</code> two independent ways to fail. Either the cast returns <code>nullptr</code> and the program crashes on the dereference, or the cast succeeds and the call runs straight into the assertion. The <code>nullptr</code> failure is worse because it occurs only for objects whose interface implementation comes from Blueprint.</p>
<h2 id="when-the-cast-is-valid">When the cast is valid</h2>
<p>A cast to <code>I*</code> covers every implementation of the interface under exactly one condition: the interface must be explicitly non-implementable in Blueprint. Then <code>bImplementedByK2</code> is never set on any entry, so <code>GetInterfaceAddress</code> and <code>ImplementsInterface</code> always agree.</p>
<div class="highlight" data-lang="cpp">
  <button type="button" class="code-copy" data-code-copy data-label="Copy" data-copied="Copied!" aria-label="Copy code to clipboard">Copy</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">UINTERFACE</span><span class="p">(</span><span class="n">BlueprintType</span><span class="p">,</span> <span class="n">meta</span><span class="o">=</span><span class="p">(</span><span class="n">CannotImplementInterfaceInBlueprint</span><span class="p">),</span> <span class="n">MinimalAPI</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UDamageSource</span> <span class="o">:</span> <span class="k">public</span> <span class="n">UInterface</span> <span class="p">{</span> <span class="n">GENERATED_BODY</span><span class="p">()</span> <span class="p">};</span></span></span></code></pre></div></div>
<div class="callout callout--tip">
  <div class="callout__title">Tip</div>
  This leads to the rule I follow: casting to <code>I*</code> is allowed only when I declare the interface myself and explicitly prevent it from being implemented in Blueprint. Everywhere else, use <code>Implements&lt;&gt;</code> plus <code>Execute_</code>.
</div>

<h2 id="verification">Verification</h2>
<p>I verified this in the editor, not just by reading the source. The probe collects three readings for the same object: <code>ImplementsInterface</code>, a cast to the interface type, and <code>GetNativeInterfaceAddress</code>. It then calls <code>Execute_</code>. The test interface allows Blueprint implementations, so the test can cover the case where the cast fails:</p>
<div class="highlight" data-lang="cpp">
  <button type="button" class="code-copy" data-code-copy data-label="Copy" data-copied="Copied!" aria-label="Copy code to clipboard">Copy</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">UINTERFACE</span><span class="p">(</span><span class="n">Blueprintable</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UPickupTarget</span> <span class="o">:</span> <span class="k">public</span> <span class="n">UInterface</span> <span class="p">{</span> <span class="n">GENERATED_BODY</span><span class="p">()</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">IPickupTarget</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">GENERATED_BODY</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">UFUNCTION</span><span class="p">(</span><span class="n">BlueprintNativeEvent</span><span class="p">,</span> <span class="n">Category</span> <span class="o">=</span> <span class="s">&#34;Probe&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="n">OnPicked</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Control group: native implementation.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">UCLASS</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">ANativePickup</span> <span class="o">:</span> <span class="k">public</span> <span class="n">AActor</span><span class="p">,</span> <span class="k">public</span> <span class="n">IPickupTarget</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">GENERATED_BODY</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">virtual</span> <span class="kt">void</span> <span class="n">OnPicked_Implementation</span><span class="p">()</span> <span class="k">override</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">UE_LOG</span><span class="p">(</span><span class="n">LogTemp</span><span class="p">,</span> <span class="n">Warning</span><span class="p">,</span> <span class="n">TEXT</span><span class="p">(</span><span class="s">&#34;[probe] OnPicked from C++&#34;</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">UCLASS</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UInterfaceProbe</span> <span class="o">:</span> <span class="k">public</span> <span class="n">UBlueprintFunctionLibrary</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">GENERATED_BODY</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">UFUNCTION</span><span class="p">(</span><span class="n">BlueprintCallable</span><span class="p">,</span> <span class="n">Category</span> <span class="o">=</span> <span class="s">&#34;Probe&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">static</span> <span class="kt">void</span> <span class="n">Probe</span><span class="p">(</span><span class="n">UObject</span><span class="o">*</span> <span class="n">Obj</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">UInterfaceProbe</span><span class="o">::</span><span class="n">Probe</span><span class="p">(</span><span class="n">UObject</span><span class="o">*</span> <span class="n">Obj</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">Obj</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="kt">bool</span> <span class="n">bImplements</span> <span class="o">=</span> <span class="n">Obj</span><span class="o">-&gt;</span><span class="n">GetClass</span><span class="p">()</span><span class="o">-&gt;</span><span class="n">ImplementsInterface</span><span class="p">(</span><span class="n">UPickupTarget</span><span class="o">::</span><span class="n">StaticClass</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">    <span class="n">IPickupTarget</span><span class="o">*</span> <span class="n">AsInterface</span> <span class="o">=</span> <span class="n">Cast</span><span class="o">&lt;</span><span class="n">IPickupTarget</span><span class="o">&gt;</span><span class="p">(</span><span class="n">Obj</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span><span class="o">*</span> <span class="n">NativeActor</span> <span class="o">=</span> <span class="n">Obj</span><span class="o">-&gt;</span><span class="n">GetNativeInterfaceAddress</span><span class="p">(</span><span class="n">UPickupTarget</span><span class="o">::</span><span class="n">StaticClass</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">UE_LOG</span><span class="p">(</span><span class="n">LogTemp</span><span class="p">,</span> <span class="n">Warning</span><span class="p">,</span> <span class="n">TEXT</span><span class="p">(</span><span class="s">&#34;[probe] %s | Implements=%s | Cast=%s | NativeActor=%s&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="o">*</span><span class="n">Obj</span><span class="o">-&gt;</span><span class="n">GetClass</span><span class="p">()</span><span class="o">-&gt;</span><span class="n">GetName</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">        <span class="n">bImplements</span> <span class="o">?</span> <span class="n">TEXT</span><span class="p">(</span><span class="s">&#34;true&#34;</span><span class="p">)</span> <span class="o">:</span> <span class="n">TEXT</span><span class="p">(</span><span class="s">&#34;false&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">AsInterface</span> <span class="o">?</span> <span class="n">TEXT</span><span class="p">(</span><span class="s">&#34;ptr&#34;</span><span class="p">)</span> <span class="o">:</span> <span class="n">TEXT</span><span class="p">(</span><span class="s">&#34;NULL&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">NativeActor</span> <span class="o">?</span> <span class="n">TEXT</span><span class="p">(</span><span class="s">&#34;ptr&#34;</span><span class="p">)</span> <span class="o">:</span> <span class="n">TEXT</span><span class="p">(</span><span class="s">&#34;NULL&#34;</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">bImplements</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">IPickupTarget</span><span class="o">::</span><span class="n">Execute_OnPicked</span><span class="p">(</span><span class="n">Obj</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>The second implementation of the same interface is created in the editor: a Blueprint Actor class named <code>BP_BlueprintPickup</code>, with the <code>Pickup Target</code> interface added under Class Settings and an <code>On Picked</code> event that prints <code>[probe] OnPicked from BP</code>.</p>
<figure class="bp-embed">
  <div class="bp-embed__canvas" data-bp-src="/blueprints/interface-cast-bp-pickup-event.txt" data-bp-height="340"></div>
  <noscript>
    <p class="bp-embed__fallback">Viewing the graph requires JavaScript.</p>
    <a class="bp-embed__download" href="/blueprints/interface-cast-bp-pickup-event.txt" download>Download Blueprint graph (.txt)</a>
  </noscript><figcaption class="bp-embed__caption">The BP_BlueprintPickup event graph. This On Picked implementation does not exist in any C&#43;&#43; file, and it is what makes the class&#39;s entry in UClass::Interfaces carry bImplementedByK2 = true.</figcaption></figure>
<p>The Level Blueprint of an empty map drives the test. <code>NativePickup</code> is spawned in <code>BeginPlay</code>. <code>BP_BlueprintPickup</code> is placed in the level and retrieved with <code>Get Actor of Class</code>. Both objects pass through the same <code>Probe</code> along a single execution chain:</p>
<figure class="bp-embed">
  <div class="bp-embed__canvas" data-bp-src="/blueprints/interface-cast-probe-beginplay.txt" data-bp-height="460"></div>
  <noscript>
    <p class="bp-embed__fallback">Viewing the graph requires JavaScript.</p>
    <a class="bp-embed__download" href="/blueprints/interface-cast-probe-beginplay.txt" download>Download Blueprint graph (.txt)</a>
  </noscript><figcaption class="bp-embed__caption">Level Blueprint of the test map. One BeginPlay, two actors, the same Probe function. The test compares native and Blueprint interface implementations.</figcaption></figure>
<p>The log from that run:</p>
<div class="highlight" data-lang="log">
  <button type="button" class="code-copy" data-code-copy data-label="Copy" data-copied="Copied!" aria-label="Copy code to clipboard">Copy</button><pre tabindex="0" class="ue-log"><code><span class="ue-log__line ue-log__line--warning"><span class="ue-log__cat">LogTemp</span>: <span class="ue-log__sev ue-log__sev--warning">Warning</span>: <span class="ue-log__msg">[probe] NativePickup | Implements=true | Cast=ptr | NativeActor=ptr</span>
</span><span class="ue-log__line ue-log__line--warning"><span class="ue-log__cat">LogTemp</span>: <span class="ue-log__sev ue-log__sev--warning">Warning</span>: <span class="ue-log__msg">[probe] OnPicked from C++</span>
</span><span class="ue-log__line ue-log__line--warning"><span class="ue-log__cat">LogTemp</span>: <span class="ue-log__sev ue-log__sev--warning">Warning</span>: <span class="ue-log__msg">[probe] BP_BlueprintPickup_C | Implements=true | Cast=NULL | NativeActor=NULL</span>
</span><span class="ue-log__line"><span class="ue-log__cat">LogBlueprintUserMessages</span>: <span class="ue-log__msg">[probe] OnPicked from BP</span>
</span></code></pre>
</div>
<p>In the third line, <code>Implements=true</code> sits next to <code>Cast=NULL</code> for the same object in the same call. The fourth line shows <code>Execute_</code> invoking the implementation despite the <code>NULL</code>. The <code>LogBlueprintUserMessages</code> category confirms that the call landed in the Blueprint graph rather than the native fallback.</p>
<p>The first two lines are the control group. For a native implementation, all three readings agree and the cast works. The comparison focuses on where the interface implementation comes from.</p>
<h2 id="limits">Limits</h2>
<p>An interface class declared in Blueprint rather than in C++ — that is, one without <code>CLASS_Native</code> — behaves differently. For such an interface, <code>GetInterfaceAddress</code> never enters the loop over <code>Interfaces</code>. It returns <code>this</code>, provided <code>ImplementsInterface</code> is true. That is a separate branch of the same function in <code>Engine/Source/Runtime/CoreUObject/Private/UObject/UObjectBaseUtility.cpp</code>.</p>
<div class="callout callout--warning">
  <div class="callout__title">Warning</div>
  <code>CastChecked&lt;IMyInterface&gt;</code> does not make this pattern safe. It only changes where the failure occurs. With <code>DO_CHECK</code> enabled, it raises a fatal error, which is the behavior you want. In a build without <code>DO_CHECK</code>, the unchecked definition of <code>CastChecked</code> in <code>Casts.h</code> takes over and returns whatever <code>GetInterfaceAddress</code> gives it, without any check. That definition is itself marked <code>FUNCTION_NON_NULL_RETURN</code>, so it promises the caller a non-null pointer while potentially handing back <code>nullptr</code>. On that basis, the compiler is free to remove null checks at the call site. The behavior becomes least predictable in exactly the configuration where it is hardest to diagnose.
</div>

<p>This also has consequences for testing. A test built entirely from C++ classes will never catch the problem. Every implementation is native, <code>bImplementedByK2</code> is <code>false</code> everywhere, and the cast works every time. You can see that in the first two lines of the log above, where the control group passes. A test must include a Blueprint that implements the interface; otherwise, it exercises only the variant that was already correct.</p>
<p>As for the limits of the evidence: the mechanism comes from reading the UE 5.7 source (<code>UObjectBaseUtility.cpp</code>, <code>Class.cpp</code>, <code>Casts.h</code>, and the UHT generator) and was confirmed by one run in the editor on the same version. I have not checked whether <code>PointerOffset</code> and the <code>bImplementedByK2</code> filter behave the same way in older engine branches.</p>
<h2 id="sources">Sources</h2>
<ul>
<li><code>UObjectBaseUtility::GetInterfaceAddress</code>, <code>GetNativeInterfaceAddress</code> — <code>Engine/Source/Runtime/CoreUObject/Private/UObject/UObjectBaseUtility.cpp</code></li>
<li><code>UClass::ImplementsInterface</code> — <code>Engine/Source/Runtime/CoreUObject/Private/UObject/Class.cpp</code></li>
<li><code>Cast</code> / <code>CastChecked</code> for interfaces — <code>Engine/Source/Runtime/CoreUObject/Public/Templates/Casts.h</code></li>
<li><code>Execute_</code> generation — <code>Engine/Source/Programs/Shared/EpicGames.UHT/Exporters/CodeGen/UhtHeaderCodeGeneratorCppFile.cs</code>, <code>AppendInterfaceCallFunction</code></li>
<li><a href="https://dev.epicgames.com/documentation/en-us/unreal-engine/interfaces-in-unreal-engine" data-external rel="noopener" target="_blank">Unreal Docs: Interfaces</a> — the official description of <code>UINTERFACE</code> and <code>Execute_</code> calls</li>
</ul>
]]></content:encoded>
      
    </item>
    
  </channel>
</rss>
