I have a few lines that compile well on my system but don't compile on a colleagues system. That's why I would like to ask what the go-to solution for the problem would look like. I have to deal with a enum that implicitly defines how much space I have to provide for an std::array. Other parts of the code also make use of FooSize being static. (Optimization)
My current implementation looks like this
enum class FooType
{
ShortFoo,
LongFoo
};
// defined in a different file
template <FooType FType>
class FooContainer
{
public:
static const unsigned int FooSize {(FType == FooType::ShortFoo) ? 32 : 64 };
std::array<float, FooSize> fooArray;
};
The code seems to create issues on older llvm / clang compilers. 32 and 64 are actually provided via pre processor defines. I could just skip the FooType and use the size as an template argument but I would like to know what the most reliable method of initializing FooSize would be.
Your code seems correct to me and compile without problems with my olds g++ (4.9.2) and clang++ (3.5).
But, according the error message, could be that your compiler doesn't correctly support the C++11 declaration/initialization of static data members
I suggest you to try in the following way
template <FooType FType>
class FooContainer
{
public:
static const unsigned int FooSize;
std::array<float, FooSize> fooArray;
};
template <FooType FType>
int unsigned const FooContainer<FType>::FooSize
= ((FType == FooType::ShortFoo) ? 32 : 64);
or (I suppose is better)
template <FooType FType>
class FooContainer
{
public:
static const unsigned int FooSize {(FType == FooType::ShortFoo) ? 32 : 64 };
std::array<float, FooSize> fooArray;
};
template <FooType FType>
int unsigned const FooContainer<FType>::FooSize;
You can also try defining FooSize as constexpr instead of const.
Another solution could be transform FooSize in a template parameter
template <FooType FType,
std::size_t FooSize = (FType == FooType::ShortFoo) ? 32 : 64 >
class FooContainer
{
public:
std::array<float, FooSize> fooArray;
};
Related
I'm working on some C++ code for an embedded platform (an ATTiny, though I don't think that matters) which miraculously can compile with C++17. I am trying to use constexpr and template arguments anywhere possible in order to avoid managing array sizes manually while also avoiding memory allocations, while also producing something that's difficult to express an invalid state with in case coworkers need to modify it later.
I have a particular data type which needs to be able to store a variable number of values (varies in the source code, not during runtime) which would be iterated through at runtime. Using the template array constexpr size trick and a template argument, I can define an array with an initializer list and then pass it to the object being constructed with a template size argument as follows:
template <typename T, size_t n>
constexpr size_t array_size(const T (&)[n]) { return n; }
uint16_t data_fast_flash[] = { 100, 400 };
LedPattern<array_size(data_fast_flash)> fast_flash{data_fast_flash};
uint16_t data_slow_flash[] = { 100, 900 };
LedPattern<array_size(data_slow_flash)> slow_flash{data_slow_flash};
uint16_t data_double_flash[] = { 100, 200, 100, 1000 };
LedPattern<array_size(data_double_flash)> double_flash{data_double_flash};
The LedPattern class is as follows, though I'm not sure it's relevant:
template <size_t N>
class LedPattern : public ILedPattern {
public:
explicit LedPattern(uint16_t* delay) : delay_(delay), count_(N) {}
void reset() override { index_ = 0; };
[[nodiscard]] uint16_t current() const override { return *(delay_ + index_); }
void next() override {
if (++index_ >= count_) index_ = 0;
};
private:
uint16_t* delay_;
size_t count_;
size_t index_{};
};
All of the above works as expected, but I was hoping to compress the definitions to something where the creation and naming of the uint16_t arrays could go away, given that I think they're now the most typo-prone part of the setup.
I did find some seemingly related solutions, but they all rely on the standard library, which I don't have on this platform, such as one using std::index_sequence, and another using std::forward.
With C++17 but without the standard library, is there some way of constructing something functionally similar with templates that would:
Only require some sort of initializer list of values
Could preferably deduce the number of elements in the value list itself
Would not require any allocations at runtime
Something roughly like the following, though I'm not attached to this exact syntax and I have no problem changing how LedPattern works internally:
LedPattern fast_flash{100, 400};
LedPattern slow_flash{100, 900};
LedPattern double_flash{100, 200, 100, 1000};
This seems like a job for user-defined-deduction guides. You could do something like the following:
#include <concepts>
#include <cstddef>
#include <cstdint>
using std::size_t;
using std::uint16_t;
template<size_t N>
class LedPattern {
public:
template<typename ...D>
explicit LedPattern(D...d) : delay_{uint16_t(d)...}, count_(N) {}
private:
uint16_t delay_[N];
size_t count_;
size_t index_{};
};
template<std::convertible_to<uint16_t> ...D>
LedPattern(D...) -> LedPattern<sizeof...(D)>;
LedPattern pat{100, 200, 300};
Just replace std::convertible_to<uint16_t> with typename if your compiler doesn't support concepts yet.
So I'm trying to implement small object optimization in a project of mine, but I'm running into a strange compiler error. Here's some simplified code that reproduces the issue:
#include <type_traits>
template<typename T>
class Wrapper {
T thing;
public:
Wrapper(T&& thing) {
// ...
}
};
class Test {
static const size_t PADDING_SIZE = 64;
public:
template<
typename T,
std::enable_if_t<sizeof(Wrapper<std::decay_t<T>>) <= PADDING_SIZE, int> = 0
// Error on this line ^
>
Test(T&& thing) {
new (padding) Wrapper<std::decay_t<T>>(std::forward<T>(thing));
}
char padding[PADDING_SIZE];
};
int main() {
auto t = Test(0.0f);
}
Basically, I need to take an arbitrary object, put it in a wrapper, and instantiate an instance of the wrapper in the padding, but I need to use one wrapper for types that can fit in the padding and a different one for types that are too large (one of the wrappers stores the object in place while the other allocates external space for it). And obviously I'd like to support perfect forwarding.
Unfortunately, VS2017 gives me the following compiler error: error C2027: use of undefined type 'Wrapper<decay<_Ty>::type>'. I can compile it just fine with Wrapper<T> rather than Wrapper<std::decay_t<T>>, but I think I need to use the decayed type. Clang compiles it fine as is.
So what's the problem here? I'm a bit stuck.
Possibly a bug in the VS compiler.
I can get it to compile with a slightly different form of the sfinae condition, using a default type instead of a default value:
#include <type_traits>
#include <new>
template<typename T>
class Wrapper {
T thing;
public:
Wrapper(T&& ) {
// ...
}
};
class Test {
static const size_t PADDING_SIZE = 64;
public:
template<
typename T,
class = std::enable_if_t<sizeof(Wrapper<std::decay_t<T>>) <= PADDING_SIZE>
>
Test(T&& thing) {
new (padding) Wrapper<std::decay_t<T>>(std::forward<T>(thing));
}
char padding[PADDING_SIZE];
};
int main() {
auto t = Test(0.0f);
}
No real explanation of why this should work better, it's just the form I usually use.
Ok suppose I have a struct like so:
struct Example{
uint8_t var0;
uint32_t var1;
uint16_t var2;
};
And suppose I have an instance (note constexpr) of the class that looks like this:
constexpr Example exp = {10,11,12};
And I want to somehow get the bit representation of this into a template parameter pack.
An example of what this would look like:
typedef SomeTemplate<
/* first variable: 1 byte */ 10,
/* second variable: 4 bytes */ 11, 0, 0, 0,
/* third variable: 2 bytes */ 12, 0> mydef;
My knee jerk response to doing this is to do some template meta programming magic with some unions, but unfortunately this is not possible because accessing a union in the way I want is undefined behavior at compile time (ie error).
My ultimate goal with all of this is to put an instance of a user defined type into as a template parameter....something like this:
template<Example exp>
class OtherClass{
};
So the reason I am on this rabbit trail is I could take class Example and give a templated constructor which would take a list of uint8_ts in and then initialize itself that way (thus still constexpr and effectively allowing me to pass it in directly).
So is there any way to convert an arbitrary struct instance into an bit pattern at compile time (and all constexpr)?
Here is what I eventually ended up doing:
template<class T,class... Args_t>
struct Statifier{
constexpr T operator()() { return T(Args_t()...); }
constexpr operator T() { return T(Args_t()...); }
};
And then you use it like so:
template<class T, T value>
using ic = integral_constant<T,value>;
struct Point{
int x;
int y;
int z;
constexpr Point(int x,int y,int z) : x(x),y(y),z(z) { }
};
typedef Statifier<Point,ic<int,12>,ic<int,12>,ic<int,12> > triple;
triple is now a type and can be passed into a template parameter.
Limits of this approach:
You can only use types which can be differentiated by their constructors
You have to use types that have constexpr constructors
It can be cumbersome to initialize
But it is still awesome.
Thanks for you comments....they did help me find this method
I came across some code that has confused me. I have distilled the confusion down to a simple case
template<typename T>
struct S
{
T member;
static const size_t size;
};
struct U { int i; };
typedef S<U> US_t;
template<> const size_t US_t::size = sizeof(U);
Q1: Why do I need the "template<>" on that last line, since US_t is fully described?
The code I actually came across - which compiles - has the equivalent of this:
template<>template<> const size_t US_t::size = sizeof(U);
I'm pretty sure it was a cut-and-paste error that wasn't caught because it compiled.
Q2: Is this valid? If so, why?
[I notice that codepad.org will compile the code with "template<>template<>".]
Imagine your typedef as a macro that's expanded at compile time instead of pre-processor time.
Then without the template<> you'd have
const size_t S<U>::size = sizeof(U);
And that's not a valid syntax, because you're doing a template specialization
I am using C++ (not 11) and using some libraries which have different typedefs for integer data types. Is there any way I can assert that two typedefs are the same type? I've come up with the following solution myself.. is it safe?
Thanks
template<typename T>
struct TypeTest
{
static void Compare(const TypeTest& other) {}
};
typedef unsigned long long UINT64;
typedef unsigned long long UINT_64;
typedef unsigned int UINT_32;
int main()
{
TypeTest<UINT64>::Compare(TypeTest<UINT64>()); // pass
TypeTest<UINT64>::Compare(TypeTest<UINT_64>()); // pass
TypeTest<UINT64>::Compare(TypeTest<UINT_32>()); // fail
}
In C++11, you could use std::is_same<T,U>::value.
Since you don't have C++11, you could implement this functionality yourself as:
template<typename T, typename U>
struct is_same
{
static const bool value = false;
};
template<typename T>
struct is_same<T,T> //specialization
{
static const bool value = true;
};
Done!
Likewise you can implement static_assert1 as:
template<bool> struct static_assert;
template<> struct static_assert<true> {}; //specialization
Now you can use them as:
static_assert<is_same<UINT64,UINT64>::value>(); //pass
static_assert<is_same<UINT64,UINT32>::value>(); //fail
Or you could wrap this in a macro as:
#define STATIC_ASSERT(x) { static_assert<x> static_assert_failed; (void) static_assert_failed; }
then use as:
STATIC_ASSERT(is_same<UINT64,UINT64>::value); //pass
STATIC_ASSERT(is_same<UINT64,UINT32>::value); //pass
If you use macro, then you would see the following string in the compiler generated message if the assert fails:
static_assert_failed
which is helpful. With the other information in the error message, you would be able to figure out why it failed.
Hope that helps.
1. Note that in C++11, static_assert is an operator (which operates at compile-time), not a class template. In the above code, static_assert is a class template.
Since you don't have C++11, use boost.
BOOST_STATIC_ASSERT(boost::is_same<T, U>::value);
You can write some kind of your assert function, instead of BOOST_STATIC_ASSERT.
std::type_info might help you.