65.9K
CodeProject 正在变化。 阅读更多。
Home

全套 getopt 移植,支持 Unicode 和多字节 Microsoft Visual C、C++ 或 MFC 项目

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.87/5 (49投票s)

2011 年 2 月 12 日

LGPL3

5分钟阅读

viewsIcon

249091

downloadIcon

22289

支持 getopt、getopt_long 和 getopt_long_only 以及 POSIXLY_CORRECT 环境变量标志

引言

编写此软件之前,花费了大量时间搜索健壮的 Microsoft C 和 C++ 版 getopt 实现,但结果却令人失望。

此软件是 Free Software Foundation, Inc. getopt 库用于解析命令行参数的一个修改版本,其目的是提供一个对 Microsoft Visual C 更友好的派生版本。源代码提供了对 Unicode 和多字节构建的支持,并且支持 getoptgetopt_longgetopt_long_only。此外,该代码还支持 POSIXLY_CORRECT 环境变量标志。该库在定义 getoptgetopt_longgetopt_long_only 函数时使用 _UNICODE 预处理器指令。这些函数分别映射回 getopt_a/getopt_wgetopt_long_a/getopt_long_wgetopt_long_only_a/getopt_long_only_w。此改进是为了允许在多字节和 Unicode 项目中使用单个 DLL。

原始的 GNU 代码使用了包含大量特定于 Linux 环境的预处理器指令的多个头文件和实现文件,这些文件已被删除。在删除了不必要的依赖项后,将其精简为一个头文件和一个实现文件,可以将其编译成 DLL、LIB,或者直接包含到任何 Visual C、C++ 或 MFC 项目中。getopt 库可以被专有软件使用,但是;需要采取某些措施来确保专有代码遵守 Lesser GNU Public License(对非衍生作品)。请参阅本文档的许可部分,了解有关此软件许可方式的更多详细信息。

为了简洁起见,本文档不讨论如何使用 getopt 函数。任何不熟悉 getopt 函数使用的人都应参考 GNU 教程来使用 getopt

许可

由于 getopt 在 LGPL 许可下,在某些限制条件下可以免费用于专有软件。当将此库作为专有软件解决方案的一部分使用时,重要的是将库作为动态链接库 (DLL) 使用,并且不要静态链接或直接编译到专有源代码中。静态链接要求您的软件发布为 GPL。因此,通过动态链接库 (DLL) 将库单独引用,允许在不更改使用该库的专有软件的情况下修改和更新该 DLL;在这种情况下,专有软件被认为“使用”该库。因此,它不被视为衍生作品,可以根据任何许可自由分发。

预处理器定义

getopt 编译为动态链接库 (DLL) 需要预处理器定义 EXPORTS_GETOPTEXPORTS_GETOPT 的定义将内部预处理器定义 _GETOPT_API 设置为 __declspec(dllexport) 的值。将 getopt 编译为静态库 (LIB) 或直接在项目中包含源文件和头文件需要预处理器定义 STATIC_GETOPTSTATIC_GETOPT 的定义会清除内部预处理器定义 _GETOPT_API 的值。编译使用 getopt.dll 的软件需要不使用任何库特定的预处理器定义。当不使用任何库特定的预处理器定义时,赋给内部预处理器定义 _GETOPT_API 的值是 __declspec(dllimport)

下面的代码段演示了上述逻辑

#if defined(EXPORTS_GETOPT) && defined(STATIC_GETOPT)
    #error "The preprocessor definitions of EXPORTS_GETOPT 
        and STATIC_GETOPT can only be used individually"
#elif defined(STATIC_GETOPT)
#pragma message("Warning static builds of getopt violate the Lesser GNU Public License")
    #define _GETOPT_API
#elif defined(EXPORTS_GETOPT)
    #pragma message("Exporting getopt library")
    #define _GETOPT_API __declspec(dllexport)    
#else
    #pragma message("Importing getopt library")
    #define _GETOPT_API __declspec(dllimport)
#endif

位于 getopt.h 中的以下代码段负责映射 getoptgetopt_longgetopt_long_only 函数的正确版本。附加 _a 的 getopt 函数表示使用 char 类型的 ANSI 字符,而 Unicode 函数附加 _w 表示使用 wchar_t 类型的宽字符。

#ifdef _UNICODE
    #define getopt getopt_w
    #define getopt_long getopt_long_w
    #define getopt_long_only getopt_long_only_w
    #define option option_w
    #define optarg optarg_w
#else
    #define getopt getopt_a
    #define getopt_long getopt_long_a
    #define getopt_long_only getopt_long_only_a
    #define option option_a
    #define optarg optarg_a
#endif

提供的示例代码

为了帮助理解如何使用该代码,提供了多个版本供下载。提供的下载如下

  • Visual Studio .NET 2022 ANSI 项目
  • Visual Studio .NET 2010 ANSI 项目
  • Visual Studio .NET 2008 ANSI 项目
  • Visual Studio .NET 2008 MFC 项目
  • Visual Studio .NET 2005 ANSI 项目
  • Visual Studio .NET 2005 MFC 项目
  • Visual Studio 6 ANSI 项目
  • Visual Studio 6 MFC 项目

Using the Code

使用方法与 GNU getopt 完全相同。

#include <stdio.h>
#include <stdlib.h>
#include "tchar.h"
#include "getopt.h"

int _tmain(int argc, TCHAR** argv)
{
    static int verbose_flag;
    int c;

    while (1)
    {        
        static struct option long_options[] =
        {
            {_T("verbose"), ARG_NONE, &verbose_flag, 1},
            {_T("brief"),   ARG_NONE, &verbose_flag, 0},
            {_T("add"),     ARG_NONE, 0, _T('a')},
            {_T("append"),  ARG_NONE, 0, _T('b')},
            {_T("delete"),  ARG_REQ,  0, _T('d')},
            {_T("create"),  ARG_REQ,  0, _T('c')},
            {_T("file"),    ARG_REQ, 0 , _T('f')},
            { ARG_NULL , ARG_NULL , ARG_NULL , ARG_NULL }
        };

        int option_index = 0;
        c = getopt_long(argc, argv, _T("abc:d:f:"), long_options, &option_index);

        // Check for end of operation or error
        if (c == -1)
            break;

        // Handle options
        switch (c)
        {
        case 0:
            /* If this option set a flag, do nothing else now. */
            if (long_options[option_index].flag != 0)
                break;
            _tprintf (_T("option %s"), long_options[option_index].name);
            if (optarg)
                _tprintf (_T(" with arg %s"), optarg);
            _tprintf (_T("\n"));
            break;

        case _T('a'):
            _tprintf(_T("option -a\n"));
            break;

        case _T('b'):
            _tprintf(_T("option -b\n"));
            break;

        case _T('c'):
            _tprintf (_T("option -c with value `%s'\n"), optarg);
            break;

        case _T('d'):
            _tprintf (_T("option -d with value `%s'\n"), optarg);
            break;

        case _T('f'):
            _tprintf (_T("option -f with value `%s'\n"), optarg);
            break;

        case _T('?'):
            /* getopt_long already printed an error message. */
            break;

        default:
            abort();
        }
    }

    if (verbose_flag)
        _tprintf (_T("verbose flag is set\n"));


    if (optind < argc)
    {
        _tprintf (_T("non-option ARGV-elements: "));
        while (optind < argc) _tprintf (_T("%s "), argv[optind++]);
        _tprintf (_T("\n"));
    }
    return 0;
}     

将此代码与 C++ 预编译头文件一起使用

当在具有预编译头文件的 C++ 项目中静态使用此代码时,有必要将 getopt.c 重命名为 getopt.cpp,以规避以下编译器错误

"C1853 - Precompiled header file is from a previous version of the compiler, 
or the precompiled header is C++ and you are using it from C (or vice versa)."

此外,必须将预编译头文件作为 getopt.cgetopt.cpp 文件的第一个 include。例如,如果您使用 "stdafx.h" 作为预编译头文件,则应如下所示

// File comments removed
#include "stdafx.h"
#define _CRT_SECURE_NO_WARNINGS
#include <stdlib.h>
#include <stdio.h>
#include "getopt.h"

历史

  • 2011 年 3 月 2 日 - 初始发布
  • 2011 年 2 月 20 日 - 修复了 L4 编译器警告
  • 2011 年 7 月 5 日 - 添加了 no_argumentrequired_argumentoptional_argument 定义
  • 2011 年 8 月 5 日 - 修复了导致运行时异常的非参数运行时错误
  • 2011 年 8 月 9 日 - 添加了导出 DLL 和 LIB 函数的代码
  • 2012 年 2 月 15 日 - 修复了在实现文件中缺少 _GETOPT_THROW 定义的问题
  • 2012 年 8 月 3 日 - 为 char 和 wchar_t 字符创建了单独的函数,因此单个 DLL 可以同时支持 Unicode 和 ANSI
  • 2012 年 10 月 15 日 - 修改以匹配最新的 GNU 功能
  • 2015 年 6 月 19 日 - 修复了由 option_a (255) 和 option_w (65535) 结构 val 变量导致的选项最大限制
  • 2022 年 9 月 24 日 - 更新以匹配最新的 getopt 版本
  • 2022 年 9 月 25 日 - 修复了 wchar_t* 的内存分配 (malloc 调用) 问题
© . All rights reserved.