Help us understand the problem. What is going on with this article?

列挙体と列挙子をリネームしたいが既存コードを書き換えたくない場合の一時しのぎ (C++)

表題の通り。

一時しのぎの例

もっと良い方法がある人は教えて下さい:relaxed::raised_hand:

#include <iostream>
#include <cassert>


/* 旧バージョンの列挙体と列挙子
enum class HogeEnum
{
    Taro,
    Jiro,
    Sabro
};
*/


/* 新バージョンの列挙体と列挙子。旧バージョン相当のコードを書き換えることなく、この新定義を使いたい */
enum class NewHogeEnum
{
    NewTaro,
    NewJiro,
    NewSabro
};


/* HogeEnum を NewHogeEnum 型のエイリアスとすることはできるが、列挙子の表記は旧バージョンを引き継げないためコンパイルエラー
using HogeEnum = NewHogeEnum;
*/


/* 列挙子の表記は旧バージョンを引き継げるが、HogeEnum を NewHogeEnum 型のエイリアスとすることはできないためコンパイルエラー
namespace HogeEnum {
    constexpr NewHogeEnum Taro = NewHogeEnum::NewTaro;
    constexpr NewHogeEnum Jiro = NewHogeEnum::NewJiro;
    constexpr NewHogeEnum Sabro = NewHogeEnum::NewSabro;
}
*/


/* "HogeEnum::Taro" のようなコードにおいて、 Taro は NewHogeEnum の列挙子ではないためコンパイルエラー。 
using HogeEnum = NewHogeEnum;
constexpr NewHogeEnum Taro = NewHogeEnum::NewTaro;
constexpr NewHogeEnum Jiro = NewHogeEnum::NewJiro;
constexpr NewHogeEnum Sabro = NewHogeEnum::NewSabro;
*/

/* コンパイル成功。ただし必要に応じて演算子オーバロード等の定義を追加する必要がある。そもそも型として全く別物を定義することになる
struct HogeEnum
{
    static constexpr NewHogeEnum Taro = NewHogeEnum::NewTaro;
    static constexpr NewHogeEnum Jiro = NewHogeEnum::NewJiro;
    static constexpr NewHogeEnum Sabro = NewHogeEnum::NewSabro;

    HogeEnum(const NewHogeEnum& NewHogeEnumVar) : value(NewHogeEnumVar) {};

    HogeEnum& operator=(const NewHogeEnum& NewHogeEnumVar)
    {
        value = NewHogeEnumVar;
        return *this;
    }

    operator int() const { return static_cast<int>(value); }

    bool operator ==(const NewHogeEnum& NewHogeEnumVar) { return (value == NewHogeEnumVar); }
    bool operator !=(const NewHogeEnum& NewHogeEnumVar) { return (value != NewHogeEnumVar); }

private:
    NewHogeEnum value = NewHogeEnum::NewTaro;
};
constexpr NewHogeEnum HogeEnum::Taro;
constexpr NewHogeEnum HogeEnum::Jiro;
constexpr NewHogeEnum HogeEnum::Sabro;
*/


/* コンパイル成功。ただしコード中のすべての Taro、Jiro、Sabro という文字列が置換されてしまう */
using HogeEnum = NewHogeEnum;
#define Taro NewTaro
#define Jiro NewJiro
#define Sabro NewSabro


int main() 
{
    static_assert(static_cast<int>(HogeEnum::Taro) == 0, "");
    static_assert(static_cast<int>(HogeEnum::Jiro) == 1, ""); 
    static_assert(static_cast<int>(HogeEnum::Sabro) == 2, ""); 

    HogeEnum hoge = HogeEnum::Taro;
    assert(hoge == HogeEnum::Taro);
    std::cout << static_cast<int>(hoge) << std::endl;

    hoge = HogeEnum::Jiro;
    assert(hoge == HogeEnum::Jiro);
    std::cout << static_cast<int>(hoge) << std::endl;

    hoge = HogeEnum::Sabro;
    assert(hoge == HogeEnum::Sabro);
    std::cout << static_cast<int>(hoge) << std::endl;

    HogeEnum fuga(hoge);
    assert(fuga == HogeEnum::Sabro);
    hoge = fuga;
    assert(hoge == HogeEnum::Sabro);
    std::cout << static_cast<int>(hoge) << std::endl;

    return 0;
}

using による列挙型の、マクロによる列挙子のエイリアスをそれぞれ定義するのが (思いついた限りでは) 一番良い方法に見えたがどうなんだろうか。

マクロによる一時しのぎが通用しないケース

上記コードにこんなコメントを書いた

コード中のすべての Taro、Jiro、Sabro という文字列が置換されてしまう

実際、下記のように改造したコードではマクロによる一時しのぎは通用せずコンパイルエラーを引き起こす。

#include <iostream>
#include <cassert>


/* 旧バージョンの列挙体と列挙子
enum class HogeEnum
{
    Taro,
    Jiro,
    Sabro
};
*/


/* 新バージョンの列挙体と列挙子。旧バージョン相当のコードを書き換えることなく、この新定義を使いたい */
enum class NewHogeEnum
{
    NewTaro,
    NewJiro,
    NewSabro
};


/* 新規に追加した列挙体定義。これに起因するコンパイルエラーが起きる */
enum class FugaEnum
{
    Taro,
    Jiro,
    Sabro
};


/* FugaEnum の列挙子 Taro、Jiro、Sabro も置換されてしまうのでコンパイルエラー */
using HogeEnum = NewHogeEnum;
#define Taro NewTaro
#define Jiro NewJiro
#define Sabro NewSabro


int main() 
{
    static_assert(static_cast<int>(HogeEnum::Taro)  == 0, "");
    static_assert(static_cast<int>(HogeEnum::Jiro)  == 1, ""); 
    static_assert(static_cast<int>(HogeEnum::Sabro) == 2, ""); 
    static_assert(static_cast<int>(FugaEnum::Taro)  == 0, ""); // <-- ここでエラー

    ...

}

上記コードでもコンパイルに成功する方法を @SaitoAtsushi さんが教えてくれました!ありがとうございます!(詳細は本記事のコメント欄を参照)

g_fukurowl
小説とか絵をやるし文フリに出る。 http://g-takanashi.tumblr.com 内なる大型犬
https://twitter.com/g_fukurowl
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした