C / C ++ статически определенное циклическое прямое определение данных для массива

Я ищу самый известный метод для статического определения структур данных C / C ++, которые должны быть циклически связаны. Например. дерево, где и ребенок, и родитель нуждаются в указателях друг на друга.

extern struct Op op_subops[4]; // fwd ref to children of ops

struct Op
{
const char *name;
struct Op  *parent;
struct Op  *children;
};struct Op ops[128] = {
{"op",0,&op_subops[0]}
};

struct Op op_subops[4] = {
{"subop1",&ops[1],0},
{"subop2",&ops[1],0}
};

Выше компилируется (g ++ 5.2). extern Ключевое слово позволяет мне создать прямую ссылку из ops в ops_subopsи другое направление работает естественно, так как ops предшествует ops_subops,

Мне не нравится то, что я предпочел бы, чтобы оба массива были static (не создавать публично видимый символ в объектном файле).

Я мог бы использовать целочисленный индекс для одного из направлений, но это выглядит немного странно, и я бы предпочел, чтобы компоновщик разрешил мне адрес.

У кого-нибудь есть волшебное ключевое слово, чтобы сделать эту работу?

Спасибо!

РЕДАКТИРОВАТЬ: мне нужно избегать таких вещей, как статические конструкторы C ++ и новейшие расширения C ++ 17 (к сожалению). И мой подход должен быть независимым от платформы.

2

Решение

В C или C ++ суть в том, что вы не можете ссылаться на объект до того, как объявите его, но оба языка позволяют вам писать «предварительные объявления», которые объявляют имя и (возможно, неполный) тип без определяющий предмет. Будучи более знакомым с C, я отвечу с этой точки зрения; некоторые семантики могут отличаться в C ++.

В C основная проблема с кодом, который вы представили, заключается в том, что он противоречит требованию, что для массивов

Тип элемента должен быть завершен всякий раз, когда указан тип массива.

(C2011, 6.2.5 / 20)

Это означает, что, хотя вы можете объявить массив вперед op_subops без размера (оставляя его тип незавершенным в этом смысле), вы не можете выдать какое-либо объявление для него перед типом struct Op определено. Это можно решить, просто поменяв местами две декларации.

Исправив проблему упорядочения, в C вполне нормально объявить оба массива с помощью static связь; Вы просто должны быть уверены, что если какой-либо объект объявлен более одного раза (т.е. op_subops) что все декларации согласны с его связью:

struct Op
{
const char *name;
struct Op  *parent;
struct Op  *children;
};

static struct Op op_subops[4]; // fwd ref to children of ops

static struct Op ops[128] = {
{"op",0,&op_subops[0]}
};

static struct Op op_subops[4] = {
{"subop1",&ops[0],0},
{"subop2",&ops[1],0}
};

Вы можете опустить длину op_subop если хотите, из первой декларации, и на самом деле я бы порекомендовал вам сделать это, чтобы избежать необходимости согласования длины в двух декларациях.

Хотя вы спрашиваете о «наилучшем [известном] методе», это будет вопрос мнения, окрашенного контекстом, а вопросы мнения здесь не по теме. С другой стороны, вам кажется, что в этом случае вам нужен массив для дочерних элементов, поэтому я не вижу, какие еще могут быть альтернативы без изменения типов.

2

Другие решения

Поскольку вы ищете объявление статических массивов и не можете принять их как глобальные, я предполагаю, что вы намерены сделать так, чтобы оба массива оставались недоступными для внешнего мира.

Идея 1: просто (forward-) объявить статический массив:

Сначала я подумал, что это решение для прямого заявления. Это хорошо скомпилировано с gcc для файла C: онлайн демо 1. Но, к сожалению, он не компилируется ни на MSVC, ни на C ++, потому что это не стандартно:

    static struct Op op_subops[];  // compiler dependent - it's standard!

На самом деле все проще: просто объявлять массив с его размером:

    static struct Op op_subops[4]; // as simple as that

Онлайн демо 2

Последующая инициализация не определяет / переопределяет массив; он обозначает уже объявленный массив для инициализации. Фактически, вы можете оставить измерение массива вне инициализации. И на этот раз он компилируется на gcc и на MSVC:

    static struct Op op_subops[] = {  // [] or [4]
...
};

Идея 2: поместить оба массива в безымянное пространство имен (только C ++)

Тогда вам больше не придется декорировать элементы как статические: безымянное пространство имен гарантирует, что другие модули компиляции не смогут ссылаться на эти объекты

1

По вопросам рекламы [email protected]