开发者

How can I force a c++ class method to accept only a handful of integer literals?

开发者 https://www.devze.com 2023-04-06 23:20 出处:网络
There is a class I\'m refactoring which currently has a method: void resize(size_t sz) In the current codebase, sz is always 0,1,2,or 3.

There is a class I'm refactoring which currently has a method:

void resize(size_t sz)

In the current codebase, sz is always 0,1,2,or 3. The underlying class is changing from dynamic allocation to a preallocated array of maxsize==3.

How can I get a build time error if someone tries to resize to sz>3 ? It is easy enough to add a runtime check. But I'd rather get a compile-time check that fails sooner.

I don't want to change any existing code that makes calls with an integer literal that is in-bounds ,e.g.:

x.resize(2)

should still compile as is.

But if someone comes along and tries to

x.resize(4)
  or
x.resize(n)

it should fail to compile or link.

I was thinking about a template specialized on int that is undefined for anything other than {0,1,2,3}. But I'm not sure quite how to make it do what I want within the confines of standard c++.

edit:

I should elaborate on my thoughts of using the template. I am quite willing to change the declaration of the resize function. I am not willing to change the calling code.

e.g. I was thinking something like

void resize( ConstructedFromLiteral<0,3> sz)

or

void resize( ConstructedFromLiteral<0> sz)
void resize( ConstructedFromLiteral<1> sz)
void resize( ConstructedFromLiteral<2> sz)
void resize( ConstructedFromLiteral&l开发者_如何学Ct;3> sz)


There's no way you can get a compile-time check for a run-time value. Imagine if you said,

resize(read_some_number_from_disk());

How is the compiler supposed to check that?

However, you can make the function a template, since template parameters are known at compile time:

class Foo
{
  template <unsigned int N> void resize()
  {
    static_assert(N < 4, "Error!");
    //...
  }

  //...

};

If you don't have static asserts, you can rig up your own static-assert class that'll fail to compile:

template <bool> struct ecstatic_assert;  // no definition!
template <> struct ecstatic_assert<true> { } ;

Usage:

... resize ... { ecstatic_assert<N < 4> ignore_me; /* ... */ } 


I'd use static_assert to check this at compile time:

struct foo {
  template <int N> 
  void resize() {
    static_assert(N >= 0 && N < 4);
  }
};

You get static_assert builtin in C++11, but it's easy enough to implement in C++03 too. Compared to specialisations it saves you from some quite tedious code duplication.


You can't do it. You can't keep function calls to resize as is, and check n at compile time since its a runtime value. You will need to refactor your code in order to get a compile time error (for instance std/boost static_assert).


You could make four public inlined specialized templates for ints {0, 1, 2, 3}. These would be simple one-line functions that call a private normal, generic function for any int.

EDIT: For the naysayers who say it can't be done using specialized templates (why the downvotes?):

class Demo {
private:
    template<int MyInt>
    void PrivateFunction() {
        cout << MyInt << endl;
    }
public:
    template<int MyInt> void PublicFunction();
    template<> void PublicFunction<0>() { PrivateFunction<0>(); }
    template<> void PublicFunction<1>() { PrivateFunction<1>(); }
    template<> void PublicFunction<2>() { PrivateFunction<2>(); }
    template<> void PublicFunction<3>() { PrivateFunction<3>(); }
};

Try calling Demo::PublicFunction<4>() and you'll get a linker error. Accomplishes the same thing and useful if you don't have / don't want to make a static_assert.

As others mentioned, not so easy to check the value of a parameter...


I don't like this answer for hopefully obvious reasons, but it does satisfy your stated requirements:

struct x {
  void true_resize(int n) { }
  template <int N> void template_resize();
};
  template<> void x::template_resize<0>() { true_resize(0); }
  template<> void x::template_resize<1>() { true_resize(1); }
  template<> void x::template_resize<2>() { true_resize(2); }
  template<> void x::template_resize<3>() { true_resize(3); }
#define resize(x) template_resize<x>();


int main () {
  x x;
  x.resize(2);
  x.resize(4);
}

In case it isn't obvious, the #define doesn't obey C++ scope rules, and so ruins the name resize for all other uses.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号