понедельник, 18 мая 2009 г.

Параметры шаблона

До чего людей доводит "тяга к прекрасному"...

Был вполне заурядный кусок кода:
namespace nmea
{
struct DefaultPolicy
{
static bool IsAllowed(const char*, size_t)
{
return true;
}
// ....
};

template<typename Policy=DefaultPolicy>
struct NmeaProcessor
{
bool Parse(IFieldReceiver* receiver)
{
// ....
return true;
}

bool Accumulate(const char* data, size_t size)
{
// ....
return true;
}
};

} // namespace nmea

struct LocalPolicy
{
static bool IsMessageAllowed(const char* data, size_t size)
{
return size >= 80 && !strncmp(data + 3, "TLL", sizeof "TLL" )
&& nmea_utils::IsCSGood(data);
}
};

void foo(IDataSource* data_src, IFieldReceiver* receiver_ptr)
{
nmea::NmeaProcessor<LocalPolicy> processor;

while(IChunkProcessor::Instance()->ReadData(buf, size) )
{
processor.Accumulate(buf, size);
}

processor.Parse(receiver_ptr);
}

По эстетическим соображениям очень хотелось локальную политику определить там, где она используется, чтобы не размазывать код по всему файлу:

void foo(IDataSource* data_src, IFieldReceiver* receiver_ptr)
{
struct LocalPolicy
{
static bool IsMessageAllowed(const char* data, size_t size)
{
return size >= 80 && !strncmp(data + 3, "TLL", sizeof "TLL" ) && nmea_utils::IsCSGood(data);
}
};

nmea::NmeaProcessor<LocalPolicy> processor;

while(IChunkProcessor::Instance()->ReadData(buf, size) )
{
processor.Accumulate(buf, size);
}

processor.Parse(receiver_ptr);
};

Результат получился вполне ожидаемый:
....\parser.cpp(121): error C2926: 'foo::LocalPolicy' : types with no linkage cannot be used as template arguments

Собственно, это не история, а только прелюдия к ней...
Я подоспел уже к "шапочному" разбору, когда народ начал называть компилятор не очень приличными словами, поскольку он не позволяет написать то, что требуется.
Ссылка на стандарт только подбавила масла в огонь, пришлось предложить "бытовую" гипотезу, доступную на "кухонном" уровне:
  • С++ обеспечивает типобезопасную компоновку

  • Следовательно, при инстанцировании шаблона пользовательским типом его сигнатура должна быть частью сигнатуры порожденного класса, чтобы обеспечить соблюдение ODR (one definition rule)

  • А вот для этого тип-параметр шаблона должен иметь эту самую сигнатуру, единую для всех единиц трансляции, то есть иметь ту самую внешнюю компоновку, что требуется стандартом (или что-то ей эквивалентное, но зачем?)

Разговоры о том, что при использовании в качестве параметра шаблона компоновка класса должна становиться внешней, пришлось оборвать, объяснив, что поведение любой системы должно быть консистентным - если, дописывая новый кусок кода, абсолютно не затрагивающий старый, мы можем изменить его семантику (например, при известном везении и совпадении имен) программа может перестать линковаться из-за внезапного появления нового класса в поле зрения компоновщика, то это есть "не айс". Я не прав?

BTW, так что c "тягой к прекрасному" пришлось ограничиться внесением LocalPolicy в анонимный namespace :-)

Комментариев нет: