-4

Here is my code in hpp:

template <size_t... Bits>
class TelemetryCompactDataStrategy : public TelemetryCommonDataStrategy
{
    static_assert((Bits + ...) <= 32, "Bit count must not exceed 32.");

public:
    static constexpr auto bitSize  = (Bits + ...);
    static constexpr auto dataSize = (bitSize + 7) / 8;
    TelemetryCompactDataStrategy(QString... paths, QObject* parent = nullptr)
        : TelemetryCommonDataStrategy({paths, ...}, dataSize, parent), m_args({paths, ...})
    {
        static_assert(sizeof...(Bits) == sizeof...(paths), "Number of bits and values must match.");
    }

    QByteArray toByteArray() override
    {
        uint32_t value = 0;
        currentIndex   = 0;
        writeBits<0>(value, m_values);

        QByteArray byteArray;
        byteArray.append(reinterpret_cast<const char*>(&value), dataSize);
        return (dataSize == m_dataSize) ? byteArray : QByteArray(m_dataSize, 0x00);
    }

    int          validTelemetryDataCount() override { return m_args.size(); }
    QVariantHash toVariantHash(QByteArray datas) override { return {}; }

protected:
    void processedTelemetryValue() override
    {
        m_values = m_rawValues;
    }

private:
    std::array<QString, sizeof...(Bits)> m_args;
    size_t                               currentIndex = 0;

    template <size_t Index>
    void writeBits(uint32_t& value, const QMap<QString, QVariant>& values)
    {
        constexpr size_t BitCount = BitsAtIndex<Index>();

        if (currentIndex + BitCount > 32)
            throw std::runtime_error("Bit count exceeds 32.");

        const auto valueToAdd = values[m_args[Index]].toInt() & ((1 << BitCount) - 1);
        value |= (valueToAdd << currentIndex);

        currentIndex += BitCount;
        if constexpr (Index + 1 < sizeof...(Bits))
            writeBits<Index + 1>(value, values);
    }

    template <size_t Index>
    static constexpr size_t BitsAtIndex()
    {
        if constexpr (Index < sizeof...(Bits))
            return Bits + ... + (Index * 0); // Force evaluation of Bits at Index
        else
            return 0;
    }
};

I want my class construct with several QString values, but when I instantiate a object like:

new TelemetryCompactDataStrategy<2, 1, 1, 2, 1>(getTelemetryDataInfo("LowBeamLight").path,
                                                        getTelemetryDataInfo("RainLight").path,
                                                        getTelemetryDataInfo("Wipers").path,
                                                        getTelemetryDataInfo("EngineIgnition").path,
                                                        getTelemetryDataInfo("TyreType").path,
                                                        this)

it seems that this does not work, how should I modify my hpp or how should I do to realise such a class to contain variety of bits like this class?

I've checked those documents on cppreference.com, It demonstrates using template<typename... Type>, I wonder if I can't use a real type like QString? this only supports templete type like func(T&& ...path)? Hope somebody save me!

wcc chn
  • 1
  • 2
  • 2
    Edit your question with the actual error you get and please try to reduce it to a [mre]. We do not know what your `TelemetryCommonDataStrategy` looks like or what `this` refers to in your usage example. – Botje Aug 11 '23 at 11:46
  • 1
    what is the meaning of "it seems that this does not work" ? – 463035818_is_not_an_ai Aug 11 '23 at 11:49
  • 3
    `QString... paths` is not a parameter pack. For [parameter packs](https://en.cppreference.com/w/cpp/language/parameter_pack) you need templates. – Some programmer dude Aug 11 '23 at 11:49
  • it looks like you are confusing [varargs](https://en.cppreference.com/w/cpp/utility/variadic), the c way of variadic functions with c++ variadic functions via variadic templates. Despite similar look and use of `...`, its two completely different stories. – 463035818_is_not_an_ai Aug 11 '23 at 11:55
  • 1
    its unclear why you think you need a parameter pack for the constructor in the first place. Use `std::vector` or `std::initializer_list` or if you want to keep the check for matching size a `std::array` – 463035818_is_not_an_ai Aug 11 '23 at 12:01
  • 2
    You already have a `std::array` member, so why not take the same type as argument in your constructor? – Nelfeal Aug 11 '23 at 12:07
  • @Botje I'm sorry about the confusing I made in the description, but my error info showed in Chinese and I think it may not help you all, the error code is C3543 and message is { "const QString&": Contains no parameter package }. About the base class (TelemetryCommonDataStrategy), it concerns nothing to this error, it just provide the m_values member object that are a QMap. – wcc chn Aug 14 '23 at 02:46
  • @463035818_is_not_an_ai I really does not know about how to used the variadic templates, this code originated from ChatGPT and i just have it a little modified, for the reason why I want to use parameter pack instead of using vector\array and so on, it's basically because it's much more a intuitive realisation, you know, how many templates I gave I will also have a constructor that contains parameters in that number. – wcc chn Aug 14 '23 at 02:55
  • @Nelfeal Because I think the variable parameter template in this requirement corresponds to a variable parameter constructor is a more intuitive implementation – wcc chn Aug 14 '23 at 02:59
  • Please don't add "solved" to the title, or any solutions to the question itself. Pressing a checkmark next to an answer is enough to mark the question as solved, and if you want to share your solution, you should post your own answer below. – HolyBlackCat Aug 14 '23 at 08:58

2 Answers2

1

You can only expend template parameter pack, so QString... paths is invalid.

Several alternatives:

  • Use std::array<QString, sizeof...(Bits)> (or other containers)

    template <size_t... Bits>
    class TelemetryCompactDataStrategy : public TelemetryCommonDataStrategy
    {
        TelemetryCompactDataStrategy(std::array<QString, sizeof...(Bits)> paths, QObject* parent = nullptr)
            : TelemetryCommonDataStrategy(paths, dataSize, parent), m_args(std::move(paths))
        {
        }
    // ...
    };
    

    calling site would have extra {..} around your QString.

  • Use some trick to "convert" Bits into QString:

    template <typename T, std::size_t>
    using always_t = T;
    
    #if 0 // Alternative for buggy compiler
    template <typename T, std::size_t>
    struct always
    {
        using type = T;
    };
    template <typename T, std::size_t I>
    using always_t = typename always<T, I>::type;
    #endif
    

    And then

    template <size_t... Bits>
    class TelemetryCompactDataStrategy : public TelemetryCommonDataStrategy
    {
        TelemetryCompactDataStrategy(always_t<QString, Bits>... paths, QObject* parent = nullptr)
            : TelemetryCommonDataStrategy({paths...}, dataSize, parent), m_args({paths...})
        {
        }
    // ...
    };
    
  • Use template constructor:

    template <size_t... Bits>
    class TelemetryCompactDataStrategy : public TelemetryCommonDataStrategy
    {
        template <typename... Ts
    #if 1 // C++17 SFINAE
                  , std::enable_if_t<,
                     sizeof...(Ts) == sizeof...(Bits) && 
                     (std::is_constructible_v<QString, Ts> && ...)
                  int> = 0
    #endif
        >
    #if 0 // For C++20, to replace SFINAE
        requires (sizeof...(Ts) == sizeof...(Bits)
              && (std::is_constructible_v<QString, Ts> && ...))
    #endif
        TelemetryCompactDataStrategy(Ts... paths, QObject* parent = nullptr)
            : TelemetryCommonDataStrategy({paths...}, dataSize, parent), m_args({paths...})
        {
        }
    // ...
    };
    
Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • "expend template" -> "expand template parameter pack" ? And afaik `QString... paths` is not invalid, its just not a parameter pack, but an ellipsis for c-like variadic functions, not what OP wants i suppose – 463035818_is_not_an_ai Aug 11 '23 at 13:18
  • In OP case, as it is the last non defaulted argument is it indeed `QString, ...`. :-/ – Jarod42 Aug 11 '23 at 13:24
  • oh right, I missed the `,` for the varargs and got mislead by some weird gcc extension. – 463035818_is_not_an_ai Aug 11 '23 at 13:26
  • Thanks for your help, I used the second trick and it did solved this error. But this class compiles still have a error of: "MSVC(C1001): internal compiler error", I have to solve this error now... Anyway, thank you so much for your solution. – wcc chn Aug 14 '23 at 03:11
  • Direct alias might be problematic with "buggy" compiler. Alternative implementation added which might avoid some ICE. – Jarod42 Aug 14 '23 at 07:36
  • One could probably do something with `std::enable_if_t` (with an always-true condition) to avoid writing a helper template for (2). – HolyBlackCat Aug 14 '23 at 08:38
0

Thanks to all of yourselves, with the help of @Jarod42 , finally my class finished like this, it compress numeric values to one uint32_t, this matches my requirement and I post it here if this may help someone. Anyway thanks all of you for your helpful advices.

template <typename T, std::size_t>
using always_t = T;

template <size_t... Bits>
class TelemetryCompactDataStrategy : public TelemetryCommonDataStrategy
{
    static_assert((Bits + ...) <= 32, "Bit count must not exceed 32.");

public:
    static constexpr auto bitSize  = (Bits + ...);
    static constexpr auto dataSize = (bitSize + 7) / 8;

    TelemetryCompactDataStrategy(always_t<QString, Bits>... paths, QObject* parent = nullptr)
        : TelemetryCommonDataStrategy({paths...}, dataSize, parent), m_args({paths...})
    {
        static_assert(sizeof...(Bits) == sizeof...(paths), "Number of bits and values must match.");
    }

    QByteArray toByteArray() override
    {
        uint32_t value = 0;
        writeBits<0, dataSize * 8>(value, m_values);

        QByteArray byteArray;
        byteArray.append(reinterpret_cast<const char*>(&value), dataSize);
        return (dataSize == m_dataSize) ? byteArray : QByteArray(m_dataSize, 0x00);
    }

    ...

private:
    std::array<QString, sizeof...(Bits)> m_args;

    /**
     * @brief 算出指定Index位置的数据有占用多少个bit
     * 
     * @tparam Index 数据的索引
     * @return constexpr size_t 数据占用的长度 bit
     */
    template <size_t Index>
    static constexpr size_t BitsAtIndex()
    {
        // 使用递归调用和模板特化来获取对应位置的模板参数值
        return std::get<Index>(std::tuple(Bits...));
    }
    /**
     * @brief 在value中写入各个位置的数据,数据集来自values
     * 
     * @tparam Index 要写入的数据的模板的index
     * @tparam offSet 写入到value的偏移bit数
     * @param value 写入到的value
     * @param values 数据来源集合
     */
    template <size_t Index, size_t offSet>
    void writeBits(uint32_t& value, const QVariantHash& values)
    {
        constexpr size_t BitCount = BitsAtIndex<Index>();

        const auto valueToAdd = values[m_args[Index]].toInt() & ((1 << BitCount) - 1);
        value |= (valueToAdd << offSet);

        if constexpr (Index + 1 < sizeof...(Bits))
            writeBits<Index + 1, offSet - BitCount>(value, values);
    }
};
wcc chn
  • 1
  • 2