声明和定义引用彼此的初始化Const结构的最佳方法

时间:2016-02-20 17:16:22

标签: c struct const

有很多关于如何定义相互引用的结构的帖子,但是我没有看到关于用这些定义的结构创建的变量的帖子。

我有许多菜单可以在我的产品中导航,由两个结构表示。此代码构建了我想要的结构:

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:landxml="http://www.landxml.org/schema/LandXML-1.2" xmlns:hexagon="http://xml.hexagon.com/schema/HeXML-1.7" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text" version="1.0" encoding="UTF-16" indent="no" omit-xml-declaration="yes"/>

    <xsl:variable name="XML" select="/"/>
    <xsl:variable name="fileExt" select="'txt'"/>
    <xsl:variable name="fileDesc" select="'Output pointno, easting, northing, and ortho height to three decimal places. + handling of linecodes e.g. (40,41)'"/>
    <xsl:template match="/">
        <xsl:for-each select="$XML">
            <xsl:for-each select="landxml:LandXML">
                <xsl:for-each select="hexagon:HexagonLandXML">
                    <xsl:for-each select="hexagon:Point">

                        <xsl:for-each select="@uniqueID">
                            <xsl:value-of select="string(.)"/>
                            <xsl:text>,</xsl:text>
                        </xsl:for-each>

                        <xsl:for-each select="hexagon:Coordinates">
                            <xsl:for-each select="hexagon:Local">
                                <xsl:for-each select="hexagon:Grid">
                                    <xsl:for-each select="@e">
                                        <xsl:value-of select='format-number(., "########.000")' />
                                        <xsl:text>,</xsl:text>
                                    </xsl:for-each>
                                    <xsl:for-each select="@n">
                                        <xsl:value-of select='format-number(., "########.000")' />
                                        <xsl:text>,</xsl:text>
                                    </xsl:for-each>
                                    <xsl:for-each select="@hghthO">
                                        <xsl:value-of select='format-number(., "########.000")' />
                                        <xsl:text>,</xsl:text>
                                    </xsl:for-each>

                                </xsl:for-each>  
                            </xsl:for-each>  
                        </xsl:for-each>

                        <xsl:for-each select="hexagon:PointCode">
                            <xsl:value-of select="string(@code)"/>
                            <xsl:text>,</xsl:text>

                            <xsl:if test="@codeLinework='open line'"  >
                                <xsl:choose>
                                    <xsl:when test="../@lineworkFlag='START LINJE'">
                                        <xsl:text>40</xsl:text>
                                        <xsl:text>,</xsl:text>
                                    </xsl:when>
                                    <xsl:when test="../following-sibling::*[1]/PointCode[@codeLinework='none']">
                                        <xsl:text>41</xsl:text>
                                        <xsl:text>,</xsl:text>
                                    </xsl:when>

                                    <!-- <xsl:when test="../@codeLinework">
                                           <xsl:text> </xsl:text>
                                           <xsl:text>,</xsl:text>
                                    </xsl:when>-->

                                </xsl:choose>
                            </xsl:if>
                            <xsl:if test="@codeLinework='none'">
                                <xsl:text>30</xsl:text>
                                <xsl:text>,</xsl:text>
                            </xsl:if>
                        </xsl:for-each>

                        <xsl:text>
</xsl:text>

                    </xsl:for-each>
                </xsl:for-each>
            </xsl:for-each>
        </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>

让我对此代码感到困惑的是“extern”关键字。我需要它来编译,但由于结构是在同一个文件中定义的,因此感觉应该有更好的方法来编写代码。我绝对不希望将所有声明都放在头文件中,因为使用终端的代码都不应该直接访问这些结构。通常我会克服错误并继续编码,但这个来源实际上会被客户看到。

有没有办法可以在没有“extern”关键字的情况下编写此代码?

2 个答案:

答案 0 :(得分:1)

在标准C中,结构不等同于typedef 这个修改过的代码在标准的C编译器上编译:

typedef void (*MENU_FUNCTION_TYPE)();

typedef enum
{
  MENU_INPUT_NAV,     // Menu just navigates to other menus.
  MENU_INPUT_ACTION,  // Menu displays the results of an action.
  MENU_INPUT_NUM,     // Menu take numeric input and processes it.
  NUM_MENU_INPUTS
} MENU_INPUT_TYPE;

// A menu is comprised of a list of menu items which you select with the number keys.
// This structure defines a menu and the menu items it contains.
typedef struct tag_MENU_ITEM_TYPE MENU_ITEM_TYPE;
typedef struct tag_MENU_TYPE
{
  PGM_P text;                         // Pointer to text to display as menu name.
  const MENU_ITEM_TYPE* menu_items;   // Pointer to structures describing each menu item in this menu.
  uint8_t num_items;                  // The number of menu item in this menu.
  MENU_INPUT_TYPE type;               // Specifies how this menu processes keyboard input.
} MENU_TYPE;

// This structure defines a menu item and what it does when you select it.
typedef struct tag_MENU_ITEM_TYPE
{
  PGM_P text;                         // Pointer to text to display as menu item name.
  const MENU_TYPE* link;              // Pointer to which menu selecting this menu item takes you.
  MENU_FUNCTION_TYPE action;          // Pointer to function that is executed when this menu item is selected.
} MENU_ITEM_TYPE;

//These now acts as forward declarations
const MENU_TYPE PROGMEM main_menu;
const MENU_TYPE PROGMEM todo_menu;
const MENU_TYPE PROGMEM todo2_menu;
const MENU_TYPE PROGMEM todo3_menu;
const MENU_TYPE PROGMEM todo4_menu;   

// **************************
// "Main Menu"
const char PROGMEM main_menu_text[] = "Main Menu";
const char PROGMEM main_menu_item_text1[] = "First Menu";
const char PROGMEM main_menu_item_text2[] = "Second Menu";
const char PROGMEM main_menu_item_text3[] = "Third Menu";
const char PROGMEM main_menu_item_text4[] = "Fourth Menu";
const MENU_ITEM_TYPE PROGMEM main_menu_items[] =
{
    {main_menu_item_text1, &todo_menu, NULL},
    {main_menu_item_text2, &todo2_menu, NULL},
    {main_menu_item_text3, &todo3_menu, NULL},
    {main_menu_item_text4, &todo4_menu, NULL}
};
const MENU_TYPE PROGMEM main_menu =
{
    main_menu_text,
    main_menu_items,
    LENGTH(main_menu_items, MENU_ITEM_TYPE PROGMEM),
    MENU_INPUT_NAV
};

// **************************
// "TODO Menu"
const char PROGMEM todo_menu_text[] = "TODO Menu";
const char PROGMEM todo_menu_item_text1[] = "todo";
const MENU_ITEM_TYPE PROGMEM todo_menu_items[] =
{
    {todo_menu_item_text1, &todo2_menu, NULL}
};
const MENU_TYPE PROGMEM todo_menu =
{
    todo_menu_text,
    todo_menu_items,
    LENGTH(todo_menu_items, MENU_ITEM_TYPE PROGMEM),
    MENU_INPUT_NAV
};

// **************************
// "TODO2 Menu"
const char PROGMEM todo2_menu_text[] = "TODO2 Menu";
const char PROGMEM todo2_menu_item_text1[] = "todo2";
const MENU_ITEM_TYPE PROGMEM todo2_menu_items[] =
{
    {todo2_menu_item_text1, &todo3_menu, NULL}
};
const MENU_TYPE PROGMEM todo2_menu =
{
    todo2_menu_text,
    todo2_menu_items,
    LENGTH(todo2_menu_items, MENU_ITEM_TYPE PROGMEM),
    MENU_INPUT_NAV
};

// **************************
// "TODO3 Menu"
const char PROGMEM todo3_menu_text[] = "TODO3 Menu";
const char PROGMEM todo3_menu_item_text1[] = "todo3";
const MENU_ITEM_TYPE PROGMEM todo3_menu_items[] =
{
    {todo3_menu_item_text1, &todo4_menu, NULL}
};
const MENU_TYPE PROGMEM todo3_menu =
{
    todo3_menu_text,
    todo3_menu_items,
    LENGTH(todo3_menu_items, MENU_ITEM_TYPE PROGMEM),
    MENU_INPUT_NAV
};

// **************************
// "TODO4 Menu"
const char PROGMEM todo4_menu_text[] = "TODO4 Menu";
const MENU_TYPE PROGMEM todo4_menu =
{
    todo4_menu_text,
    NULL,
    0,
    MENU_INPUT_NUM
};

答案 1 :(得分:0)

如果这是用户可见的代码,并且您希望使用avr-gcc -std=c99 -Wall -Wextraavr-g++ -std=c++11 -Wall -Wextra(加上-O-mmcu=等选项进行编译而不会出现错误或警告你需要),我建议你隐藏宏中的脆弱部分,并使宏健壮。例如,使用与原始问题中显示的结构非常匹配的结构:

#include <avr/pgmspace.h>
#define JOIN2TOKENS(token1, token2)  JOIN2TOKENS_(token1, token2)
#define JOIN2TOKENS_(token1, token2) token1 ## token2

struct menu_st {
    const char                      *const title;
    unsigned char                    const items;
    const struct menuitem_st *const *const item;
};

struct menuitem_st {
    const char                      *const label;
    void                           (*const action)();
    const struct menu_st            *const submenu;
};

#define DECLARE_MENU(name, titlestr) \
    extern const struct menu_st name PROGMEM; \
    static const PROGMEM char JOIN2TOKENS(title_of__, name) [] PROGMEM = titlestr

#define DECLARE_MENUITEM(name, labelstr, funcptr, menuptr) \
    static const char JOIN2TOKENS(label_of__, name) [] PROGMEM = labelstr; \
    static const struct menuitem_st name [1] PROGMEM = { { JOIN2TOKENS(label_of__, name), funcptr, menuptr } }

#define DECLARE_SUBMENU(name, labelstr, tomenu) \
    DECLARE_MENUITEM(name, labelstr, (void (*)())0, &(tomenu))

#define DECLARE_ACTION(name, labelstr, funcname) \
    DECLARE_MENUITEM(name, labelstr, funcname, (const struct menu_st *const )0)

#define DECLARE_ACTSUB(name, labelstr, funcname, tomenu) \
    DECLARE_MENUITEM(name, labelstr, funcname, &(tomenu))

#define DEFINE_MENU(name, ...) \
    static const struct menuitem_st *const JOIN2TOKENS(menu_items_of__, name) [] PROGMEM = { __VA_ARGS__ }; \
    const struct menu_st name PROGMEM = { \
        JOIN2TOKENS(title_of__, name), \
        sizeof (JOIN2TOKENS(menu_items_of__, name)) / sizeof (JOIN2TOKENS(menu_items_of__, name)[0]), \
        JOIN2TOKENS(menu_items_of__, name), \
    }


/* 0. Declare or define menu action functions.
 *    These are declared as static, so that they are only visible
 *    in the current compilation unit.
 *
 *    As an example, action_1() and action_2() are defined here,
 *    but defaults() is only declared.
*/
static void action_1(void) { return; }
static void action_2(void) { return; }
static void defaults(void);

/* 1. Declare each menu.
 *
 *    DECLARE_MENU(name, string)
 *        'name' is the menu variable name, and
 *        'string' is the menu title, a string literal.
*/
DECLARE_MENU(main_menu, "Main menu");
DECLARE_MENU(submenu_1, "First submenu");
DECLARE_MENU(submenu_2, "Second submenu");

/* 2. Declare each menu item. Below,
 *        'name' is the local variable name (used in DEFINE_MENU()),
 *        'string' is the text for this menu item,
 *        'function' is the function called when the menu item is activated, and
 *        'menu' is the name of the menu the menu item takes to.
 *
 *    DECLARE_SUBMENU(name, string, menu)
 *        Declare a menu item, that takes to another menu.
 *
 *    DECLARE_ACTION(name, string, function)
 *        Declare a menu item, that causes a function to be called.
 *
 *    DECLARE_ABTSUB(name, string, function, menu)
 *        Declare a menu item, that causes a function to be called,
 *        and then changes to another menu.
 *
*/
DECLARE_SUBMENU(to_main_menu, "Back to main menu", main_menu);
DECLARE_SUBMENU(to_submenu_1, "To first submenu",  submenu_1);
DECLARE_SUBMENU(to_submenu_2, "To second submenu", submenu_2);
DECLARE_ACTION(do_action_1,   "Action 1", action_1);
DECLARE_ACTION(do_action_2,   "Action 2", action_2);
DECLARE_ACTSUB(do_defaults,   "Reset to defaults", defaults, main_menu);

/* 3. Define which menus have which menu items.
 *
 *    DEFINE_MENU(name, menu_item_1 [, ..., menu_item_N ])
 *        'name' is the menu variable name, and should have been
 *               declared using DECLARE_MENU(name, string) before.
 *        'menu_item_1' (and all additional parameters) are
 *               menu items declared before using
 *               DECLARE_SUBMENU(), DECLARE_ACTION(), or DECLARE_ACTSUB().
*/    
DEFINE_MENU(main_menu, to_submenu_1, to_submenu_2);
DEFINE_MENU(submenu_1, to_submenu_2, do_action_1, do_action_2, to_main_menu);
DEFINE_MENU(submenu_2, to_submenu_1, do_defaults, to_main_menu);

/* 4. If the menu action functions were only declared earlier,
 *    define them here.
*/
static void defaults(void) { return; }

DECLARE_MENU()宏确实使用extern const来转发声明菜单结构。只有上面的菜单名称 - main_menusubmenu_1submenu_2 - 在外部可见。这恰好在C99和C ++ 11中都能正常工作。如果您希望这些符号是本地符号,那么您需要为C99和C ++ 11提供不同的代码(可以通过#ifdef __cplusplus - #else - #endif来实现,但很难看,如果你问我)。

avr-gcc-4.8.2 -std=c99 -Wall -Wextraavr-g++-4.8.2 -std=c++11 -Wall -Wextra都存储了.progmem.data部分中的所有数据结构。

请注意,由于GCC不支持多个地址空间(我最后检查的是avr-gcc-4.8.2),因此您必须使用pgm_read_byte()pgm_read_word()中定义的宏{1}}访问数据结构。例如,您需要<avr/pgmspace.h>而不是main_menu.items。类似下面的示例 - 但请注意,此部分未经测试,可能包含拼写错误/ thinkos,尤其是在取消引用指针时:

pgm_read_byte(&(main_menu.items))

在我看来,使用指针会使代码非常密集。一种完全不同的方法,一种使用数组索引来识别菜单,另一种方法来识别选项,对我来说似乎更为可取。

相关问题