简体   繁体   English

C ++函数模板专门化的二进制搜索

[英]c++ Binary search of a function template specialization

I am trying to write a factory with a compile-time class registration. 我正在尝试编写一个具有编译时类注册的工厂。 The idea is simple: we get an object by id using a template specialization. 这个想法很简单:我们使用模板专门化通过id获得对象。 Here the code: 这里的代码:

#include <iostream>

typedef unsigned short IdType;
#define MAX_ID_TOO_COMPLEX 10000
#define MAX_ID 100
#define MIN_ID 1

static_assert(MAX_ID >= MIN_ID, 
    "error: max message id is lesser than min");
static_assert(std::is_signed<IdType>::value || MIN_ID != 0, 
    "error: for unsigned types min must be positive");

class Base
{
public:
    virtual ~Base(){};
};

class Derv : public Base
{
public:
    static const IdType id;
};
const IdType Derv::id = 4;


template<IdType _Left = MIN_ID, IdType _Right = MAX_ID>
Base * create(IdType id)
{
    const IdType _Mid = (_Left + _Right) / 2;
    if (_Mid == id)
        return create<_Mid>();
    if (_Mid < id)
        return create<_Mid + 1, _Right>(id);
    return create<_Left, _Mid - 1>(id);
}
template<>
Base * create<MAX_ID + 1, MAX_ID>(IdType id)
{
    std::cout << " too much " << std::endl;
    return nullptr;
}

template<>
Base * create<MIN_ID, MIN_ID - 1>(IdType id)
{
    std::cout << " too less " << std::endl;
    return nullptr;
}


template<IdType _Id>
Base * create()
{
    std::cout <<  "no registered class for " << _Id << std::endl;
    return nullptr;
}


//registering the Derv class
template<>
Base * create<Derv::id>()
{
    std::cout << "creating Derv..." << std::endl;
    return new Derv;
}

int main()
{
    for (IdType i = 0; i < MAX_ID + 2;  i++) {
        Base * x = create(i);
        if (x != nullptr) delete x;
    }
    system("pause");
}

I use the binary search instead of iterations to avoid the problem described here: What does it mean if Visual studio 2012 throws a compile error that shouldn't exist for VS2012? 我使用二进制搜索而不是迭代来避免此处描述的问题: 如果Visual Studio 2012抛出了VS2012不应该存在的编译错误,这意味着什么?

This code works as it should, but when the max id is over 10000 I got the next error: 这段代码可以正常工作,但是当最大id超过10000时,我得到下一个错误:

fatal error C1128: number of sections exceeded object file format limit : compile with /bigobj

So, it creates a template instantiation for each possible _Left/_Right combination. 因此,它为每种可能的_Left / _Right组合创建一个模板实例化。

I am wondering if there is a way to search without enlarging of the object file? 我想知道是否有一种无需扩大目标文件即可进行搜索的方法? Or is there any more elegant way to do the same? 还是有其他更优雅的方法可以做到这一点?

UPD: UPD:

Slightly modificated code from @Yakk answer below: 来自@Yakk答案的略微修改的代码如下:

#include <iostream>

typedef unsigned short IdType;
#define MAX_ID_TOO_COMPLEX 500
#define MAX_ID 100
#define MIN_ID 0

static_assert(MAX_ID >= MIN_ID,
    "error: max id is lesser than min");

class Base
{
public:
    virtual ~Base() {};
};

class Derv : public Base
{
public:
    static const IdType id;
};
const IdType Derv::id = 4;

template<IdType id>
Base* create() {
    std::cout << "no registered class for " << id << std::endl;
    return nullptr;
}

//registering the Derv class
template<>
Base * create<Derv::id>()
{
    std::cout << "creating Derv..." << std::endl;
    return new Derv;
}


namespace details {
    template<IdType min_id, IdType...ids>
    Base* createById(IdType x, std::integer_sequence<IdType, ids...>) {
        using base_maker = Base*(*)();
        static const base_maker makers[] {
            create<min_id + ids>...
        };
        return makers[x - min_id]();
    }
}

template<IdType min_id = MIN_ID, IdType max_id=MAX_ID>
Base* createById(IdType x) {
    if (x < min_id) return nullptr;
    if (x >= max_id) return nullptr;
    return details::createById<min_id>(x, std::make_integer_sequence<IdType, max_id - min_id>{});
}


int main()
{
    for (IdType i = 0; i < MAX_ID + 2; i++) {
        Base * x = createById(i);
        if (x != nullptr) delete x;
    }

    system("pause");
}

Seems much better. 似乎好多了。

Use a jump table instead of your binary search. 使用跳转表而不是二进制搜索。

template<IdType id>
Base* create() {
  return nullptr;
}
namespace details {
  template<IdType min_id, IdType...ids>
  Base* create(IdType x, std::integral_sequence<IdType, ids...>) {
    using base_maker = Base*(*)();
    static constexpr const base_maker makers[] = {
      create<min_id+ids>...
    };
    return makers[x-min_id]();
  }
}
template<IdType min_id, IdType max_id>
Base* create(IdType x) {
  if (x < min_id) return nullptr;
  if (x >= max_id) return nullptr;
  return details::create<min_id>(x, std::make_integral_sequence<IdType, max_id-min_id>{} );
}

this creates 2 functions, and a single jump table. 这将创建2个函数和一个跳转表。 It then does math to find the function to call, and calls it. 然后,它会进行数学运算以找到要调用的函数,然后对其进行调用。

std::make_integral_sequence etc is not available until C++14, but it is easy to write one. std::make_integral_sequence等直到C ++ 14才可用,但是很容易编写。 There are many implementations on stack overflow. 堆栈溢出有很多实现。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM