tuple操作について

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
新米

tuple操作について

#1

投稿記事 by 新米 » 7年前

お世話になっております
C++14 勉強中ですが、
例えば、tupleの中にいろいろobjectを入れてから、ある関数func(STD::tuple t)の引数として渡されて、
func(STD::tuple t)の中で tに保存している要素(object)を一つ一つ取り出して、各要素の型に応じて
設定したり、処理したりします。

いろいろ調べたところ、tuple中の要素を取り出すには std::get<N>( t ); を使うしかないようです。
それに、Nで要素位置を指定しますが、常数値でなければなりません。

例えば、tupleの中に50個の要素が格納されているとすれば、

auto& v0 = std::get<0>( t );
[v0の型を判定して何等かの処理をする]

auto& v1 = std::get<1>( t );
[v1の型を判定して何等かの処理をする]

auto& v2 = std::get<2>( t );
[v2の型を判定して何等かの処理をする]

:: ::

auto& v49 = std::get<49>( t );
[v49の型を判定して何等かの処理をする]

のように一一コードを書かなければなりませんね。

for (int n = 0 ; n<50; n++) {
auto& v = std::get<n>( t ); // 現実この文は無理ですけれども
vの型を判定して何等かの処理をする;
}

のような感覚で処理できる方法はないでしょうか。

アバター
a5ua
記事: 199
登録日時: 13年前

Re: tuple操作について

#2

投稿記事 by a5ua » 7年前

Boost.Fusionが使えるかもしれません。

サンプルコードです。
http://ideone.com/CaHpiH

新米

Re: tuple操作について

#3

投稿記事 by 新米 » 7年前

情報ありがとうございます。
教えていただいたBoostのやり方でで試したら、うまくできました!

でも、C++14自体でできないでしょうか。

アバター
tk-xleader
記事: 158
登録日時: 13年前
連絡を取る:

Re: tuple操作について

#4

投稿記事 by tk-xleader » 7年前

boostなしでというのであればこんな感じでしょうかね。

コード:

#include <iostream>
#include <tuple>
using namespace std;

template<std::size_t I>
class do_tuple_exec{
	template<typename T,std::size_t J>
	struct impl{
		template<typename Func,typename... Types>
		static void exec(std::tuple<Types...>& t, Func f){
			f(std::get<J>(t));
			impl<T,J+1>::exec(t,f);
		}
	};
	template<typename T>
	struct impl<T,I>{
		template<typename Func,typename... Types>
		static void exec(std::tuple<Types...>& t, Func f){
		}
	};
public:	
	template<typename Func,typename... Types>
	static void exec(std::tuple<Types...>& t,Func f){
		impl<int,0>::exec(t,f);
	}
};


template<typename Func,typename... Types>
void tuple_exec(std::tuple<Types...>& t,Func f){
	do_tuple_exec<std::tuple_size<std::tuple<Types...>>::value>::exec(t,f);
}

int main() {
	std::tuple<int,double,bool> t(100,3.14,true);
	tuple_exec(t,[](auto v){std::cout<<v<<std::endl;});
	return 0;
}

アバター
tk-xleader
記事: 158
登録日時: 13年前
連絡を取る:

Re: tuple操作について

#5

投稿記事 by tk-xleader » 7年前

上のコードの簡単な説明です。

 テンプレートでループを実現しようという場合、テンプレートを再帰的に特殊化するというテクニックが一般的に利用されます。そこでテンプレート引数に整数を取るようにして、愚直に可変長テンプレートの数まで再帰し続けるように実装したのが上のコードです。
 下のようなコードと同じような発想に基づいています。

コード:

void do_func(int* array,int array_size,int now){
	if(now == array_size) return ;
	printf("%d",array[now]);
	do_func(array,array_size,now+1);
}
void func(int* array,int array_size){
	do_func(array,array_size,0);
}

sleep

Re: tuple操作について

#6

投稿記事 by sleep » 7年前

新米 さんが書きました: for (int n = 0 ; n<50; n++) {
auto& v = std::get<n>( t ); // 現実この文は無理ですけれども
vの型を判定して何等かの処理をする;
}

のような感覚で処理できる方法はないでしょうか。

コード:

#include <iostream>
#include <string>
#include <tuple>
#include <utility>
#include <typeinfo>
using namespace std;


void func(const int& v)
{
	cout << v << " の型は、" << typeid(int).name() << " です。" << endl;
}

void func(const double& v)
{
	cout << v << " の型は、" << typeid(double).name() << " です。" << endl;
}

void func(const char*& v)
{
	cout << v << " の型は、" << typeid(const char*).name() << " です。" << endl;
}

void func(const string& v)
{
	cout << v << " の型は、" << typeid(string).name() << " です。" << endl;
}


template<typename Tuple, typename F, size_t... I>
void for_each_impl(Tuple t, F&& f, index_sequence<I...>)
{
	using tasks = int[];
	tasks{ (void(std::forward<F>(f)(std::get<I>(t))), 0)... };
}

template<typename Tuple, typename F>
void for_each(Tuple t, F&& f)
{
	for_each_impl(t, std::forward<F>(f), make_index_sequence<tuple_size<Tuple>::value>());
}

int main()
{
	auto t = make_tuple(7, "hello", 823.75, string("hahaha"));

	for_each(t, [](auto& v)
	{
		func(v);
	});
}

新米

Re: tuple操作について

#7

投稿記事 by 新米 » 7年前

tk-xleader 様、 sleep 様
すごいコードを作って頂いてありがとうございます。

本当に皆さんのコードを拝見して本当に感心致しました。

でも、理解には少々時間かかりますので、後で報告させていただきます。

新米

Re: tuple操作について

#8

投稿記事 by 新米 » 7年前

tk-xleader 様

参考させていただいたコードの中に、
下記の処にコンパイルエラーが発生しました[Visual Studio 2015]

コード:

 f( std::get<J>( t ) );//  error C2784, error C2672 等発生 : e.g., “operator __surrogate_func”: no matching overloaded function found.
 impl<T , J + 1>::exec( t , f );//c:\program files (x86)\microsoft visual studio 14.0\vc\include\utility(446): error C2338: tuple index out of bounds
原因ご存じであれば、教えてくださいませんか。

アバター
tk-xleader
記事: 158
登録日時: 13年前
連絡を取る:

Re: tuple操作について

#9

投稿記事 by tk-xleader » 7年前

 GCCおよびClangでは普通に通りますから、コードそのものに問題があるわけではないはずなのですが、Visual C++は標準準拠度が他に比べると低いといわざるを得ないので、おそらくVisual C++側の問題ではないかと思います。
 ただ、こちらでもコンパイルをして、エラーメッセージを確認したところ、どうもdo_tuple_exec::implの実体化の段階で、特殊化されているはずのクラステンプレートが実体化されず、特殊化されていないバージョンが実体化されているようです。おそらくエラーの原因はこれだと思います。

新米

Re: tuple操作について

#10

投稿記事 by 新米 » 7年前

tk-xleader 様へ
さっそくお返答ありがとうございます。
事情了解致しました。
高度なアイディアとプログラミング技法にいい勉強になりました。

sleep 様へ
ご提供いただいたコードは本当に素晴らしいです。
Visual studio 2015でまったく問題なく実行できました。
それにアイディアの高さとプログラミング技法の高さとともに感服しました。
Don't wake up !  (笑)

新米

Re: tuple操作について

#11

投稿記事 by 新米 » 7年前

sleep 様
お世話になっております。
下記の関数の中の文①と文②の意味をご解説お願いできますか。

コード:


void for_each_impl(Tuple t, F&& f, index_sequence<I...>)
{
    using tasks = int[];//  -----------①
    tasks{ (void(std::forward<F>(f)(std::get<I>(t))), 0)... };  // ----------②
}



sleep

Re: tuple操作について

#12

投稿記事 by sleep » 7年前

新米 さんが書きました: 下記の関数の中の文①と文②の意味をご解説お願いできますか。

コード:

void for_each_impl(Tuple t, F&& f, index_sequence<I...>)
{
    using tasks = int[];//  -----------①
    tasks{ (void(std::forward<F>(f)(std::get<I>(t))), 0)... };  // ----------②
}
①は、typedef
②は、int型配列のリスト初期化

②は、std::initializer_list でも代用可

コード:

template<typename Tuple, typename F, size_t... I>
void for_each_impl(Tuple t, F&& f, index_sequence<I...>)
{
	std::initializer_list<int>{ (void(std::forward<F>(f)(std::get<I>(t))), 0)... };
}
理解へのヒントとなるコードを載せておきます。

コード:

#include <iostream>
#include <string>
#include <tuple>
#include <utility>
#include <typeinfo>
using namespace std;


void func(const int& v)
{
	cout << v << " の型は、" << typeid(int).name() << " です。" << endl;
}

void func(const double& v)
{
	cout << v << " の型は、" << typeid(double).name() << " です。" << endl;
}

void func(const char*& v)
{
	cout << v << " の型は、" << typeid(const char*).name() << " です。" << endl;
}

void func(const string& v)
{
	cout << v << " の型は、" << typeid(string).name() << " です。" << endl;
}


int main()
{
	auto t = make_tuple(7, "hello", 823.75, string("hahaha"));


	auto lambda = [](auto& v) { func(v); };

	// using tasks = int[];//  -----------①
	typedef int tasks[];

	// tasks{ (void(std::forward<F>(f)(std::get<I>(t))), 0)... };  // ----------②
	tasks  // std::initializer_list<int> でも代用可
	{
		// voidへのキャストは、コンパイラへ常に lambda の戻り値を使用しないことを明示
		(static_cast<void>(lambda(std::get<0>(t))), 0),
		(static_cast<void>(lambda(std::get<1>(t))), 0),
		(static_cast<void>(lambda(std::get<2>(t))), 0),
		(static_cast<void>(lambda(std::get<3>(t))), 0)
	};


	cout << endl;


	//②の動作確認
	int array[]
	{
		(func("abcde"), 16),
		((void)func(1), 9, "URL", 7, 17),
		(void(3), 18),
		(123.456, 19)
	};

	for (auto i : array)
	{
		cout << i << endl;
	}
}

新米

Re: tuple操作について

#13

投稿記事 by 新米 » 7年前

sleep様
丁寧なご解説ありがとうございます。
ところが、下記の文の意味はなんですか。
もうC言語の範疇を超えたような表現ですね!
出力結果の規律性もわかりません。
すみませんが、もう一度ご説明お願い致します。

コード:

   
//②の動作確認
    int array[]
    {
        (func("abcde"), 16),
        ((void)func(1), 9, "URL", 7, 17),
        (void(3), 18),
        (123.456, 19)
    };

出力:

abcde の型は、class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > です。
1 の型は、int です。
16
17
18
19
====================
私の環境:
MS Visual studio 2015
OS Windows7

sleep

Re: tuple操作について

#14

投稿記事 by sleep » 7年前

新米 さんが書きました: もうC言語の範疇を超えたような表現ですね!
C言語でも下記のように修正すれば同じことができます。(オーバーロードが無いのと初期化構文のパターンが少ないので)

コード:

#include <stdio.h>

void func(const char* v)
{
    printf("%s\n", v);
}

int main()
{
    int array[] = 
    {
        (func("abcde"), 16),
        ((void)func("hello"), 9, "URL", 7, 17),
        ((void)3, 18),
        (123.456, 19)
    };

    for (int i = 0; i < 4; i++)
    {
        printf("%d\n", array[i]);
    }
}
これは何も特別なことをやっている訳ではありません。
変数、配列などの初期化を行う構文は言語の基礎中の基礎なので、この辺りは自身で調べてしっかり勉強しておいてください。

コード:

int array[] { 1, 2, 3, 4 };
は、

コード:

int array[] = { 1, 2, 3, 4 };
のようにも記述できます。

あと、コンマ演算子(Wikipedia)についても、C言語で使用されているものです。

コード:

#include <stdio.h>

void func(const char* v)
{
    printf("%s\n", v);
}

int main()
{
    int a = 0;
    a = (1, func("hello"), 3);
 
    printf("a = %d\n", a);
}
主題と内容がズレ始めているので、もし、これ以上に言語の仕様に関して質問がある場合は別のトピックを立ててください。
 

新米

Re: tuple操作について

#15

投稿記事 by 新米 » 7年前

sleep 様
丁寧なご教授に本当に感謝の気持ちいっぱいです。

恥ずかしながら、変数初期化にいろいろ方法があることは知りませんでした。
いい勉強になりました。
ついでにカンマ初期化法に関して
通常関数を入れても良いが、Lambda関数はダメですね。
というか、カンパラしても問題ない、run-timeエラーも出ないが、ただただ無視されるだけみたいです。

例えば、

コード:


a = (1 , [&] ( ) {b = 100 + c; } , [&] ( ) {a = b*b; c = 0; } , a + b + c);

Lambda関数の中にbとcの値を変更したが、
aの値だけ更新され、bとcは前のままでした。

閉鎖

“C言語何でも質問掲示板” へ戻る