PHPFixing
  • Privacy Policy
  • TOS
  • Ask Question
  • Contact Us
  • Home
  • PHP
  • Programming
  • SQL Injection
  • Web3.0
Showing posts with label variadic. Show all posts
Showing posts with label variadic. Show all posts

Friday, November 4, 2022

[FIXED] How to write an overload function for std::array that calls a variadic function?

 November 04, 2022     arrays, c++, lambda, variadic     No comments   

Issue

I have the following variadic function:

template <typename... Args>
CustomType<Args...> method(const Args& args...);

which works fine when I just do e.g.

method(1.0f, 2.0f, 3.0f);

However I also want to write an overload for std::array<T, N>:

template <typename T, std::size_t N>
auto method(const std::array<T, N>& arr);

Reading this post I figured I would use the following helper functions:

template <typename T, std::size_t N, typename VariadicFunc, uint32_t... I>
auto methodApply(const std::array<T, N>& arr, VariadicFunc func, std::index_sequence<I...>)
{
    return func(arr[I]...);
}

template <typename T, std::size_t N, typename VariadicFunc, typename Indices = std::make_index_sequence<N>>
auto methodArr(const std::array<T, N>& arr, VariadicFunc func)
{
    return methodApply(arr, func, Indices());
}

and then do

template <typename T, std::size_t N>
auto method(const std::array<T, N>& arr)
{
   methodArr(arr, /* what to pass here? */);
}

Note: the reason why I would like to template on func is because I want these helper functions to be generic so that I may use them for other variadic functions, not just method().

Only I'm not sure what pass as the second argument - I was thinking of a lambda function that would make a call to the original function method(const Args& args...) but it's unclear to me how to do this.

EDIT: Restricted to C++14.


Solution

You can wrap it in a lambda and let the compiler deduce the type for you

template <typename T, std::size_t N>
auto method(const std::array<T, N>& arr)
{
   return methodArr(arr, [](const auto&... args) { return method(args...); });
}

Demo

In C++17, methodApply can be replaced with std::apply

template <typename T, std::size_t N>
auto method(const std::array<T, N>& arr) {
  return std::apply(
    [](const auto&... args) { return method(args...); }, arr);
}


Answered By - 康桓瑋
Answer Checked By - Senaida (PHPFixing Volunteer)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

[FIXED] What is the difference between lambda capture [&args...] and [...args = std::forward<Args>(args)]

 November 04, 2022     c++, c++20, lambda, templates, variadic     No comments   

Issue

I'm writing a simple game with Entity Component System. One of my components is NativeScriptComponent. It contains the instance of my script. The idea is that I can create my NativeScriptComponent anytime and then Bind to it any class implementing Scriptable interface. After that my game's OnUpdate function will automatically instantiate all scripts in my game and will call their OnUpdate function.

My scripts can have their own, different constructors so I need to forward all the arguments to them when I bind my script.

Consider the following code:

#include <iostream>
#include <memory>
#include <functional>

using namespace std;

struct Scriptable
{
    virtual void OnUpdate() {};
};
struct MyScript : Scriptable 
{ 
    MyScript(int n) : value(n) {}
    void OnUpdate() override {cout << value;} 
    int value;
};

struct NativeScriptComponent
{
    unique_ptr<Scriptable> Instance;
    function<unique_ptr<Scriptable>()> Instantiate;

    template<typename T, typename ... Args>
    void Bind(Args&&... args)
    {
        // (A)
        Instantiate = [&args...]() { return make_unique<T>(forward<Args>(args)...);  };
        // (B) since C++20
        Instantiate = [...args = forward<Args>(args)]() { return make_unique<T>(args...);  };
    }
};

int main()
{
    NativeScriptComponent nsc;
    nsc.Bind<MyScript>(5);
    
    // [..] Later in my game's OnUpdate function:
    if (!nsc.Instance)
        nsc.Instance = nsc.Instantiate();
    nsc.Instance->OnUpdate(); // prints: 5

    return 0;
}

1) What is the difference between option A and B

Instantiate = [&args...]() { return make_unique<T>(forward<Args>(args)...);  };

vs

Instantiate = [...args = forward<Args>(args)]() { return make_unique<T>(args...);  };
  1. Why can't I use forward<Args>(args) inside make_unique in option B?

  2. Are both A and B perfectly-forwarded?


Solution

For added completeness, there's more things you can do. As pointed out in the other answer:

[&args...]() { return make_unique<T>(forward<Args>(args)...);  };

This captures all the arguments by reference, which could lead to dangling. But those references are properly forwarded in the body.


[...args=forward<Args>(args)]() { return make_unique<T>(args...);  };

This forwards all the arguments into the lambda, args internally is a pack of values, not references. This passes them all as lvalues into make_unique.


[...args=forward<Args>(args)]() mutable { return make_unique<T>(move(args)...);  };

Given that, as above, args is a pack of values and we're creating a unique_ptr anyway, we probably should std::move them into make_unique. Note that the lambda has its own internal args here, so moving them does not affect any of the lvalues that were passed into this function.


[args=tuple<Args...>(forward<Args>(args)...)]() mutable {
    return std::apply([](Args&&... args){
        return std::make_unique<T>(forward<Args>(args)...);
    }, std::move(args));
};

The most fun option. Before we'd either capture all the arguments by reference, or forward all the args (copying the lvalues and moving the rvalues). But there's a third option: we could capture the lvalues by reference and move the rvalues. We can do that by explicitly capturing a tuple and forwarding into it (note that for lvalues, the template parameter is T& and for rvalues it's T - so we get rvalues by value).

Once we have the tuple, we apply on it internally - which gives us Args&&... back (note the && and that this is not a generic lambda, doesn't need to be).

This is an improvement over the previous version in that we don't need to copy lvalues -- or perhaps it's worse because now we have more opportunity for dangling.

A comparison of the four solutions:

option can dangle? lvalue rvalue
&args... both lvalues and rvalues 1 copy 1 move
...args=FWD(args) and args... no 2 copies 1 move, 1 copy
...args=FWD(args) and move(args)... no 1 copy, 1 move 2 moves
args=tuple<Args...> lvalues 1 copy 2 moves


Answered By - Barry
Answer Checked By - Timothy Miller (PHPFixing Admin)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

Thursday, April 28, 2022

[FIXED] Why does va_arg(va_list, type) give me a C6285 warning?

 April 28, 2022     c++, variadic, warnings     No comments   

Issue

Everything is working as intended, and I get the values I need from va_arg(va_list, type), but I get this warning everywhere I call va_arg:

Warning C6285 (<non-zero constant> || <non-zero constant>) is always a non-zero constant. Did you intend to use the bitwise-and operator?

Example code:

void Logger::log(LogLevel level, const char* location, uint32_t line, const char* format, ...)
{
    va_list arg_ptr;
    va_start(arg_ptr, format);

    while (*format) {
        // ...
        if (*format == 'd') { // 
            int i = va_arg(arg_ptr, int); // <-- Warning is reported here
            // ...
        }
        // ...
        ++format;
    }
    // ...
    va_end(arg_ptr);
}

Why do I get this warning and how can I get rid of it?

I'm using Visual Studio Community 2019 with Visual C++ 2019


Solution

C6### error codes are IntelliSense codes. These are based on heuristics and are meant to point the attention to potential errors, but can also result in false-positives, which seems to be the case here; it's probably triggering on the va_arg implementation in the CRT:

#define __crt_va_arg(ap, t)                                               \
    ((sizeof(t) > sizeof(__int64) || (sizeof(t) & (sizeof(t) - 1)) != 0) \  // <== Here
        ? **(t**)((ap += sizeof(__int64)) - sizeof(__int64))             \
        :  *(t* )((ap += sizeof(__int64)) - sizeof(__int64)))

I would simply ignore it ...

If it bothers you, report it to the vendor: Help → Send Feedback → Report a Problem...



Answered By - rustyx
Answer Checked By - Senaida (PHPFixing Volunteer)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg
Older Posts Home

Total Pageviews

Featured Post

Why Learn PHP Programming

Why Learn PHP Programming A widely-used open source scripting language PHP is one of the most popular programming languages in the world. It...

Subscribe To

Posts
Atom
Posts
All Comments
Atom
All Comments

Copyright © PHPFixing